计算QPS-Sentinel限流算法
sentinel 前方参考
计算QPS-Sentinel限流算法 https://www.cnblogs.com/yizhiamumu/p/16819497.html
Sentinel 介绍与下载使用https://www.cnblogs.com/yizhiamumu/p/16823313.html
sentinel的四种流控规则介绍 https://www.cnblogs.com/yizhiamumu/p/16819593.html
sentinel 的限流规则及流量控制 https://www.cnblogs.com/yizhiamumu/p/16819680.html
sentinel中如何使用@SentinelResource和openFeign来进行服务熔断和降级的操作 https://www.cnblogs.com/yizhiamumu/p/16823146.html
计算QPS-Sentinel限流算法
一. Sentinel架构大致流程
1. Sentinel其实就是一个AOP,通过AspectJ切入要进行限流的接口,为其添加@Around环绕通知,并使用try-catch包裹起来,源码在SentinelAutoConfiguration中
2. 每一个对该限流接口的请求,都要经过AOP的增强,先执行过一系列流控、熔断规则组成的责任链,然后才执行真正的接口逻辑。责任链的组装使用了原生的spi机制,流控规则可以在sentinel控制台去配置,配置完毕后会填入sentinel服务端,也就是我们的某一个服务,请求流控接口时,就会触发流控逻辑!
@Aspect //使用的AOP
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {
//切入点
@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
public void sentinelResourceAnnotationPointcut() {
}
//环绕通知
@Around("sentinelResourceAnnotationPointcut()")
public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
Method originMethod = resolveMethod(pjp);
//1.获取 @SentinelResource 注解
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
if (annotation == null) {
// Should not go through here.
throw new IllegalStateException("Wrong state for SentinelResource annotation");
}
String resourceName = getResourceName(annotation.value(), originMethod);
EntryType entryType = annotation.entryType();
int resourceType = annotation.resourceType();
Entry entry = null;
try {
// 2.执行流控组成的责任链
entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
// 3.执行业务方法
Object result = pjp.proceed();
return result;
} catch (BlockException ex) {
//4. 抛出流控异常
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
// 5. 抛出业务异常
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
// The ignore list will be checked first.
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(pjp, annotation, ex);
}
// No fallback function can handle the exception, so throw it out.
throw ex;
} finally {
if (entry != null) {
//6.所有流控的收尾逻辑,比如:断路器半开状态重试
entry.exit(1, pjp.getArgs());
}
}
}
}
3. 如果执行中抛出异常,异常分为流控异常BlockException
和 业务异常Throwable
,流控异常可以使用blockHandler
进行处理,业务异常使用fallback
处理!
@RequestMapping(value = "/findOrderByUserId/{id}")
@SentinelResource(value = "findOrderByUserId",
//业务异常,ExceptionUtil类中的fallback方法来处理
fallback = "fallback",fallbackClass = ExceptionUtil.class,
//流控异常,ExceptionUtil类中的handleException方法来处理
blockHandler = "handleException",blockHandlerClass = ExceptionUtil.class
)
public R findOrderByUserId(@PathVariable("id") Integer id) {
//ribbon实现
String url = "http://xx/order/findOrderByUserId/"+id;
R result = restTemplate.getForObject(url,R.class);
if(id==4){
throw new IllegalArgumentException("非法参数异常");
}
return result;
}
public class ExceptionUtil {
//业务异常
public static R fallback(Integer id,Throwable e){
return R.error(-2,"===被异常降级啦===");
}
//流控异常
public static R handleException(Integer id, BlockException e){
return R.error(-2,"===被限流啦===");
}
}
4 Sentinel中统计单位时间QPS进行流控时,采用的是滑动时间窗算法!流控效果有三种
快速失败,直接抛出流控异常,底层使用的是滑动时间窗口算法
预热Warm up,把突然爆发的大流量变为缓慢增加
匀速排队,使用的漏桶算法
5 Sentinel中服务熔断降级有三个指标
慢调用比例
异常比例
异常个数
二. Sentinel断路器的三种状态
Sentinel中服务熔断降级的断路器有三个状态,分别是关闭(close)、打开(open)和半开(halfOpen)状态。
1 如果在单位时间内达到断路条件,则把断路器置为打开(open)状态,抛出流控异常,进行服务熔断
2 下一次请求过来时,如果断路器是关闭(close)状态,直接通行;如果是打开(open)状态,则会查看当前时间是否大于断路后的最小等待时间,如果大于则把断路器置为半开(halfOpen)状态;如果小于,继续阻塞
3 最后会在try-catch-finally的finally中判断断路器的状态是否是半开(halfOpen)状态,如果是,则请求一次接口,如果请求正常,则把断路器置为打开(open)状态,如果不正常把断路器置为关闭(close)状态
三. 计算QPS的限流算法
①:计数器限流
计数器法是限流算法里最简单也是最容易实现的一种算法。对于A接口来说,1分钟的访问次数不能超过100个。
那么可以这么做:
- 在一开始的时候,我们可以设置一个计 数器counter,每当一个请求过来的时候,counter就加1,
- 如果counter的值大于100并且该请求 与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多;
- 如果该请求与第一个请求的间 隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter
计数器实现限流的缺点就是:精度低,如果在 0.5分钟 和 1.5 分钟之间有超过100个请求,这种限流算法就不起作用了。此外,计数器限流算法的实现还可以使用redis,设置一个key,一分钟过期,进来一个请求就使用incr命令自增一,代码中拿key的值与limt进行比较!
public class _10_限流算法_计数器 {
//开始统计时间
private long beginTime = System.currentTimeMillis();
//请求数
private int reqCount = 0;
//请求限制数
private int limit = 100;
//单位时间:1分钟
private long window = 1000 * 60;
public boolean limitReq() {
//当前时间
long currTime = System.currentTimeMillis();
if (currTime < beginTime + window) {
//如果当前时间在统计期内,则递增请求数,并于限制数100比较
reqCount++;
return reqCount <= limit;
} else {
//如果当前时间不在统计期内,则重置请求数,并设置当前时间为统计开始时间
reqCount = 1;
beginTime = currTime;
return true;
}
}
}
②:滑动时间窗算法限流
为了解决计数器法统计精度太低的问题,引入了滑动窗口算法。滑动时间窗其实就是把计数器限流算法的时间窗口再做进一步划分,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。Sentinel底层在做统计QPS做快速失败时用的也是滑动时间窗算法
滑动时间窗口限流实现:
- 假设某个服务最多只能每秒钟处理100个请求,可以设置一个1秒钟的滑动时间窗口,用LinkedList表示,该窗口分为10个格子。
- 每个格子100毫秒,每100毫秒移动一次,每次移动都需要记录当前服务100ms内请求的次数counter到格子中,counter的值是累计请求的值。不会被重置
- 如果格子数大于10个,删除最前边的各自,格子数始终保留10个
- 用最后一个格子的counter值减去最前边格子的counter值,如果大于限流请求数,则会被限流。否则不做限流
public class _11_限流算法_滑动时间窗 {
//服务访问次数,可以放在Redis中,实现分布式系统的访问计数
Long counter = 0L;
//使用LinkedList来记录滑动窗口的10个格子。
LinkedList<Long> slots = new LinkedList<Long>();
public static void main(String[] args) throws InterruptedException {
_11_限流算法_滑动时间窗 timeWindow = new _11_限流算法_滑动时间窗();
//开启一个子线程执行滑动时间窗检测请求个数
new Thread(new Runnable() {
@Override
public void run() {
try {
timeWindow.doCheck();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//主线程死循环模拟一直有请求过来
while (true) {
//TODO 判断限流标记
timeWindow.counter++;
Thread.sleep(new Random().nextInt(15));
}
}
private void doCheck() throws InterruptedException {
while (true) {
//每过100ms,就把总请求数加入linkedList末尾节点,该linkedList长度也自增一
slots.addLast(counter);
//如果linkedList长度大于10个,则删除最前面的一个,体现滑动时间窗
if (slots.size() > 10) {
slots.removeFirst();
}
//比较最后一个节点和第一个节点,两者相差100以上就限流
if ((slots.peekLast() - slots.peekFirst()) > 100) {
System.out.println("限流了。。");
//TODO 修改限流标记为true
} else {
//TODO 修改限流标记为false
}
//每100毫秒执行一次
Thread.sleep(100);
}
}
}
Sentinel底层在做统计QPS做快速失败时用的也是滑动时间窗算法,只不过与上面的稍有不同
Sentinel在做QPS统计时,滑动时间窗有两个维度- 毫秒级维度:初始化一个跨度为1000ms,包含两个500ms的时间窗口
- 秒级维度:还有一个跨度为60s的,包含60个1s的时间窗口
在毫秒级维度中,仅仅使用两个时间窗口就完成了QPS的计算,并没有做删除时间窗口节点的操作,而是清空原本节点的内容。两个时间窗口代表数组的两个下标。
通过 (当前时间 / 500ms) % 数组长度2的取模结果,得到当前时间的请求数落在那个时间窗口内
然后拿 (当前时间 / 500ms) * 500ms比较时间窗口的起始位置,
如果与之前 (比如:500ms) 一致,就把当次请求数加入到该窗口内用作统计
如果与之前不一致,就清空之前时间窗口内统计的数据,并放入当前时间的请求数,完成滑动的操作
③:漏桶算法限流
首先,需要有一个固定容量的桶,有水流进来,也有水流出去。对于流进来的水来说,我们无法预计一共有多少水会流进来,也无法预计水流的速度。但是对于流出去的水来说,这个桶可以固定水流出的速率。而且,当桶满了之后,多余的水将会溢出。
将算法中的水换成实际应用中的请求,就可以看到漏桶算法天生就限制了请求的速度。 当使用了漏桶算法,可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题。
public class _12_限流算法_漏桶算法 {
//初始时间
private long initTime = System.currentTimeMillis();
//漏桶算法一般都有三个指标 桶的容量 流出速度 当前水位
private long capacity; //容量,代表最大接受请求个数
private long rate; //水流速度
private long water; //当前水位 ,桶内剩余请求数
public boolean limit() {
//当有请求进入时的时间
long now = System.currentTimeMillis();
//计算一下当时水位:请求进来之间一直在匀速滴水,当前水位要减去这部分滴出去的水!
water = Math.max(0, water - ((now - initTime) / 1000) * rate);
if (water + 1 <= capacity) {
//如果当前水位没满,返回true,并把当前水位+1
water += 1;
return true;
} else {
//否则返回false,水满了 不让进!
return false;
}
}
}
④:令牌桶限流
令牌桶算法,又称token bucket
。同样为了理解该算法,我们来看一下该算法的示意图:
从图中我们可以看到,令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的,token以 一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通过。
漏桶算法和令牌桶算法最明显的区别是令牌桶算法允许流量一定程度的突发。 因为默认的令牌桶算法,取走token是不需要耗费时间的,也就是说,假设桶内有100个token时,那么可以瞬间允许100个请求通过。
伪代码:
/**
* 令牌桶限流算法
*/
public class TokenBucket {
public long timeStamp = System.currentTimeMillis(); // 当前时间
public long capacity; // 桶的容量
public long rate; // 令牌放入速度
public long tokens; // 当前令牌数量
public boolean grant() {
long now = System.currentTimeMillis();
// 先添加令牌
tokens = Math.min(capacity, tokens + (now - timeStamp) * rate);
timeStamp = now;
if (tokens < 1) {
// 若不到1个令牌,则拒绝
return false;
} else {
// 还有令牌,领取令牌
tokens -= 1;
return true;
}
}
}
计算QPS-Sentinel限流算法的更多相关文章
- Sentinel限流实现原理
Sentinel限流的神秘面纱: 之前我们学习过限流比较主流的三种算法:漏桶,令牌桶,滑动窗口.而Sentinel采用的是最后一种,滑动窗口来实现限流的. 通过对Sentinel基础Api的使用,我们 ...
- 常用限流算法与Guava RateLimiter源码解析
在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...
- 基于.net的分布式系统限流组件(限流算法:令牌算法和漏斗算法)
转载链接:https://www.cnblogs.com/vveiliang/p/9049393.html 1.令牌桶算法 令牌桶算法是比较常见的限流算法之一,大概描述如下: 1).所有的请求在处理之 ...
- Spring Cloud Alibaba 之 Sentinel 限流规则和控制台实例
这一节我们通过一个简单的实例,学习Sentinel的基本应用. 一.Sentinel 限流核心概念 在学习Sentinel的具体应用之前,我们先来了解一下Sentinel中两个核心的概念,资源和规则. ...
- 5-4 Sentinel 限流_流控与降级
Sentinel 介绍 什么是Sentinel Sentinel也是Spring Cloud Alibaba的组件 Sentinel英文翻译"哨兵\门卫" 随着微服务的流行,服务和 ...
- Sentinel限流之快速失败和漏桶算法
距离上次总结Sentinel的滑动窗口算法已经有些时间了,原本想着一口气将它的core模块全部总结完,但是中间一懒就又松懈下来了,这几天在工作之余又重新整理了一下,在这里做一个学习总结. 上篇滑动窗口 ...
- alibaba sentinel限流组件 源码分析
如何使用? maven引入: <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>s ...
- coding++:RateLimiter 限流算法之漏桶算法、令牌桶算法--简介
RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类 <dependency> <groupId>com.google.guava</g ...
- Sentinel限流示例:编码和注解限流
一.Sentinel 是什么? 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. Sentine ...
- Alibaba Sentinel 限流与熔断初探(技巧篇)
目录 1.Sentinel 是什么 ?主要能解决什么问题? 2.限流与熔断的使用场景 3.Sentinel 源码结构 4.在 IntelliJ IDEA 中运行 Sentine Demo 温馨提示:源 ...
随机推荐
- 谈谈你对MVVM开发模式和MVT的理解?
MVVM分为Model.View.ViewModel三者. Model 代表数据模型,数据和业务逻辑都在Model层中定义: View 代表UI视图,负责数据的展示: ViewModel 负责监听 M ...
- MakeSense标注指南
1.网址 https://www.makesense.ai/ 2.操作流程 2.1 导入 点击get started 点击drop images,上传图片 选择obeject detection 新建 ...
- [oeasy]python0053_ 续行符_line_continuation_python行尾续行
续行符与三引号 回忆上次内容 上次还是转义序列 类型 英文 符号 \a bell 响铃 \b backspace 退格 \t tab 水平制表符 \v vertical tab 垂直制表符换行不回车 ...
- oeasy教您玩转vim - 66 - # 比较修改模式 vimdiff
vimdiff 回忆上次 上次有三种批量替换,分别是 :windo :bufdo :argdo 执行的{cmd}可以用|按顺序增加 update 自动更新 :set autowrite 自动写入 ...
- PowerShell 基本使用
PowerShell Basic PowerShell Basic 简要 基本使用 PowerShell cmdlet 获取帮助 PowerShell 别名和参数 编写一个 PowerShell 脚本 ...
- Packer构建openStack镜像
目录 使用Packer自动化构建镜像 使用Packer自动化构建镜像 openstack插件安装:OpenStack | Integrations | Packer | HashiCorp Devel ...
- Codeforces Round 947 (Div. 1 + Div. 2) A~H
Codeforces Round 947 (Div. 1 + Div. 2) A 模拟. B 最小的 \(a\) 肯定作为 \(i\).对于不被 \(i\) 整除的,最小的那个作为 \(j\),判断是 ...
- RHCA rh442 007 hugetlbfs strace命令追踪 脏页设置 内存分配
内存管理 虚拟内存 --- 物理内存 应用程序申请虚拟内存 --- RAM + SWAP (真正主板上的设备) 他们之间有一张映射表 page table 页表 PTE: 页表条目 虚拟内存和物理内存 ...
- 【Python】Django学习1
按黑马程序员的美多商场作方向: https://www.bilibili.com/video/BV1nf4y1k7G3 一.应用创建.注册处理.配置 Pycharm 创建Django项目: 自应用注册 ...
- 【Java】SPI机制
SPI全称: 服务供应商接口 Service Provider Interface 服务发现机制 入门概念视频来自于: https://www.bilibili.com/video/BV1E44y1N ...