Sentienl 流控效果之匀速排队与预热实现原理与实战建议
本节目录
- 1、RateLimiterController
- 2、WarmUpController
- 2.1 WarmUpController 构造函数
- 2.2 canPass 方法详解
- 3、总结
温馨提示,如果大家对源码不感兴趣,可以直接跳到本文的总结部分,了解一下预热实现原理的一些实战建议。
首先先回顾一下 Sentinel 流控效果相关的类图:
DefaultController 快速失败已经在上文详细介绍过,本文将详细介绍其他两种策略的实现原理。
首先我们应该知道,一条流控规则(FlowRule)对应一个 TrafficShapingController 对象。
1、RateLimiterController
匀速排队策略实现类,首先我们先来介绍一下该类的几个成员变量的含义:
- int maxQueueingTimeMs
排队等待的最大超时时间,如果等待超过该时间,将会抛出 FlowException。 - double count
流控规则中的阔值,即令牌的总个数,以QPS为例,如果该值设置为1000,则表示1s可并发的请求数量。 - AtomicLong latestPassedTime
上一次成功通过的时间戳。
接下来我们详细来看一下其算法的实现:
RateLimiterController#canPass
public boolean canPass(Node node, int acquireCount, boolean prioritized) {if (acquireCount <= 0) {return true;}if (count <= 0) {return false;}long currentTime = TimeUtil.currentTimeMillis();long costTime = Math.round(1.0 * (acquireCount) / count * 1000); // @1long expectedTime = costTime + latestPassedTime.get(); // @2if (expectedTime <= currentTime) { // @3latestPassedTime.set(currentTime);return true;} else {long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis(); // @4if (waitTime > maxQueueingTimeMs) { // @5return false;} else {long oldTime = latestPassedTime.addAndGet(costTime); // @6try {waitTime = oldTime - TimeUtil.currentTimeMillis(); if (waitTime > maxQueueingTimeMs) {latestPassedTime.addAndGet(-costTime);return false;}if (waitTime > 0) { // @7Thread.sleep(waitTime);}return true;} catch (InterruptedException e) {}}}return false;
}
代码@1:首先算出每一个请求之间最小的间隔,时间单位为毫秒。例如 cout 设置为 1000,表示一秒可以通过 1000个请求,匀速排队,那每个请求的间隔为 1 / 1000(s),乘以1000将时间单位转换为毫秒,如果一次需要2个令牌,则其间隔时间为2ms,用 costTime 表示。
代码@2:计算下一个请求的期望达到时间,等于上一次通过的时间戳 + costTime ,用 expectedTime 表示。
代码@3:如果 expectedTime 小于等于当前时间,说明在期望的时间没有请求到达,说明没有按照期望消耗令牌,故本次请求直接通过,并更新上次通过的时间为当前时间。
代码@4:如果 expectedTime 大于当前时间,说明还没到令牌发放时间,当前请求需要等待。首先先计算需要等待是时间,用 waitTime 表示。
代码@5:如果计算的需要等待的时间大于允许排队的时间,则返回 false,即本次请求将被限流,返回 FlowException。
代码@6:进入排队,默认是本次请求通过,故先将上一次通过流量的时间戳增加 costTime,然后直接调用 Thread 的 sleep 方法,将当前请求先阻塞一会,然后返回 true 表示请求通过。
匀速排队模式的实现的关键:主要是记录上一次请求通过的时间戳,然后根据流控规则,判断两次请求之间最小的间隔,并加入一个排队时间。
2、WarmUpController
预热策略的实现,首先我们先来介绍一下该类的几个成员变量的含义:
- double count
流控规则设定的阔值。 - int coldFactor
冷却因子。 - int warningToken
告警token,对应 Guava 中的 RateLimiter 中的 - int maxToken
double slope
AtomicLong storedTokens
AtomicLong lastFilledTime
2.1 WarmUpController 构造函数
内部的构造函数,最终将调用 construct 方法。
WarmUpController#construct
private void construct(double count, int warmUpPeriodInSec, int coldFactor) { // @1if (coldFactor <= 1) {throw new IllegalArgumentException("Cold factor should be larger than 1");}this.count = count; this.coldFactor = coldFactor; warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1); // @2maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor)); // @3slope = (coldFactor - 1.0) / count / (maxToken - warningToken);
}
要理解该方法,就需要理解 Guava 框架的 SmoothWarmingUp 相关的预热算法,其算法原理如图所示:
关于该图的详细介绍,请参考笔者的另外一篇博文:源码分析RateLimiter SmoothWarmingUp 实现原理,对该图进行了详细解读。
代码@1:首先介绍该方法的参数列表:
- double count
限流规则配置的阔值,例如是按 TPS 类型来限流,如果限制为100tps,则该值为100。 - int warmUpPeriodInSec
预热时间,单位为秒,通用在限流规则页面可配置。 - int coldFactor
冷却因子,这里默认为3,与 RateLimiter 中的冷却因子保持一致,表示的含义为 coldIntervalMicros 与 stableIntervalMicros 的比值。
代码@2:计算 warningToken 的值,与 Guava 中的 RateLimiter 中的 thresholdPermits 的计算算法公式相同,thresholdPermits = 0.5 * warmupPeriod / stableInterval,在Sentienl 中,而 stableInteral = 1 / count,thresholdPermits 表达式中的 0.5 就是因为 codeFactor 为3,因为 warm up period与 stable 面积之比等于 (coldIntervalMicros - stableIntervalMicros ) 与 stableIntervalMicros 的比值,这个比值又等于 coldIntervalMicros / stableIntervalMicros - stableIntervalMicros / stableIntervalMicros 等于 coldFactor - 1。
代码@3:同样根据 Guava 中的 RateLimiter 关于 maxToken 也能理解。
2.2 canPass 方法详解
WarmUpController#canPass
public boolean canPass(Node node, int acquireCount, boolean prioritized) {long passQps = (long) node.passQps(); // @1long previousQps = (long) node.previousPassQps(); // @2syncToken(previousQps); // @3// 开始计算它的斜率// 如果进入了警戒线,开始调整他的qpslong restToken = storedTokens.get();if (restToken >= warningToken) { // @4long aboveToken = restToken - warningToken;// 消耗的速度要比warning快,但是要比慢// current interval = restToken*slope+1/countdouble warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));if (passQps + acquireCount <= warningQps) {return true;}} else { // @5if (passQps + acquireCount <= count) {return true;}}return false;
}
代码@1:先获取当前节点已通过的QPS。
代码@2:获取当前滑动窗口的前一个窗口收集的已通过QPS。
代码@3:调用 syncToken 更新 storedTokens 与 lastFilledTime 的值,即按照令牌发放速率发送指定令牌,将在下文详细介绍 syncToken 方法内部的实现细节。
代码@4:如果当前存储的许可大于warningToken的处理逻辑,主要是在预热阶段允许通过的速率会比限流规则设定的速率要低,判断是否通过的依据就是当前通过的TPS与申请的许可数是否小于当前的速率(这个值加入斜率,即在预热期间,速率是慢慢达到设定速率的。
代码@5:当前存储的许可小于warningToken,则按照规则设定的速率进行判定。
不知大家有没有一个疑问,为什么 storedTokens 剩余许可数越大,限制其通过的速率竟然会越慢,这又怎么理解呢?大家可以思考一下这个问题,将在本文的总结部分进行解答。
我们先来看一下 syncToken 的实现细节,即更新 storedTokens 的逻辑。
WarmUpController#syncToken
protected void syncToken(long passQps) {long currentTime = TimeUtil.currentTimeMillis();currentTime = currentTime - currentTime % 1000; // @1long oldLastFillTime = lastFilledTime.get();if (currentTime <= oldLastFillTime) { // @2return;}long oldValue = storedTokens.get();long newValue = coolDownTokens(currentTime, passQps); // @3if (storedTokens.compareAndSet(oldValue, newValue)) { long currentValue = storedTokens.addAndGet(0 - passQps); // @4if (currentValue < 0) {storedTokens.set(0L);}lastFilledTime.set(currentTime);}
}
代码@1:这个是计算出当前时间秒的最开始时间。例如当前是 2020-04-06 08:29:01:056,该方法返回的时间为 2020-04-06 08:29:01:000。
代码@2:如果当前时间小于等于上次发放许可的时间,则跳过,无法发放令牌,即每秒发放一次令牌。
代码@3:具体方法令牌的逻辑,稍后详细介绍。
代码@4:更新剩余令牌,即生成的许可后要减去上一秒通过的令牌。
我们详细来看一下 coolDownTokens 方法。
WarmUpController#coolDownTokens
private long coolDownTokens(long currentTime, long passQps) {long oldValue = storedTokens.get();long newValue = oldValue;// 添加令牌的判断前提条件:// 当令牌的消耗程度远远低于警戒线的时候if (oldValue < warningToken) { // @1newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);} else if (oldValue > warningToken) { // @2if (passQps < (int)count / coldFactor) {newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);}}return Math.min(newValue, maxToken);// @3
}
代码@1:如果当前剩余的 token 小于警戒线,可以按照正常速率发放许可。
代码@2:如果当前剩余的 token 大于警戒线但前一秒的QPS小于 (count 与 冷却因子的比),也发放许可(这里我不是太明白其用意)。
代码@3:这里是关键点,第一次运行,由于 lastFilledTime 等于0,这里将返回的是 maxToken,故这里一开始的许可就会超过 warningToken,启动预热机制,进行速率限制。
3、总结
WarmUpController 这个预热算法还是挺复杂的,接下来我们来总结一下它的特征。
不知大家有没有一个疑问,为什么 storedTokens 剩余许可数越大,限制其通过的速率竟然会越慢,这又怎么理解呢?
这里感觉有点逆向思维的味道,因为一开始就会将 storedTokens 的值设置为 maxToken,即开始就会超过 warningToken,从而一开始进入到预热阶段,此时的速率有一个爬坡的过程,类似于数学中的斜率,达到其他启动预热的效果。
实战指南:注意 warmUpPeriodInSec 与 coldFactor 的设置,将会影响最终的限流效果。
为了更加直观的理解,我们举例如下,warningToken 与 maxToken 的生成公式如下:
warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));
coldFactor 设定为 3,例如限流规则中配置每秒允许通过的许可数量为 10,即 count 值等于 10,我们改变 warmUpPeriodInSec 的值来看一下 warningToken 与 maxToken 的值,以此来探究 Sentinel WarmUpController 的工作机制或工作效果。
warmUpPeriodInSec | warningToken | maxToken |
---|---|---|
1 | 5 | 10 |
2 | 10 | 20 |
3 | 15 | 30 |
4 | 20 | 40 |
根据上面的算法,如果 warningToken 的值小于 count,则限流会变的更严厉,即最终的限流TPS会小于设置的TPS。即 warmUpPeriodInSec 设置过大过小都不合适,其标准是要使得 warningToken 的值大于 count。
如果文章对你有所帮助的话,还请点个赞,谢谢。
欢迎加笔者微信号(dingwpmz),加群探讨,笔者优质专栏目录:
1、源码分析RocketMQ专栏(40篇+)
2、源码分析Sentinel专栏(12篇+)
3、源码分析Dubbo专栏(28篇+)
4、源码分析Mybatis专栏
5、源码分析Netty专栏(18篇+)
6、源码分析JUC专栏
7、源码分析Elasticjob专栏
8、Elasticsearch专栏(20篇+)
9、源码分析MyCat专栏
Sentienl 流控效果之匀速排队与预热实现原理与实战建议相关推荐
- Sentinel流控效果—Warm Up
流控效果Warm Up有什么作用? 比如有一个系统平常没人访问,突然某个时刻系统访问量达到最大:这样的话系统容易崩掉,所以需要预热,让请求慢慢的升高: 比如:秒杀系统在开启的瞬间,会有很多流量上来,很 ...
- Sentinel限流--流控模式与限流效果
文章目录 1.簇点链路 2.流控入门案例 3.流控模式:关联模式 4.流控模式:链路模式 5.流控效果:warm up 6.限流效果:排队等待 7.热点参数限流 1.簇点链路 簇点链路就是项目内的调用 ...
- Spring Cloud Alibaba:Sentinel 流控规则
文章目录 1. 前言 2. 阈值类型 2.1 QPS 2.2 线程数 3. 流控模式 3.1 直接 3.2 关联 3.3 链路 4. 流控效果 4.1 快速失败 4.2 Warm Up 4.3 排队等 ...
- sentinel 不显示项目_Sentinel+Nacos实现资源流控、降级、热点、授权
本文同名博客老炮说Java:https://www.laopaojava.com/,每天更新Spring/SpringMvc/SpringBoot/实战项目等文章资料 Sentinel+Nacos 是 ...
- Spring Cloud Alibaba 实战 | 第十二篇: 微服务整合Sentinel的流控、熔断降级,赋能拥有降级功能的Feign新技能熔断,实现熔断降级双剑合璧(JMeter模拟测试)
文章目录 一. Sentinel概念 1. 什么是Sentinel? 2. Sentinel功能特性 3. Sentinel VS Hystrix 二. Docker部署Sentinel Dashbo ...
- 什么?Sentinel流控规则可以这样玩?
点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友. 项目源码地址:公众号回复 sentinel,即可免费获取源码 前言 上一篇文章中,我们讲解了关于 ...
- Sentinel 流控(限流)
流量控制(Flow Control),原理是监控应用流量的QPS或并发线程数等指标,当达到指定阈值时对流量进行控制,避免系统被瞬时的流量高峰冲垮,保障应用高可用性. 通过流控规则来指定允许该资源通过的 ...
- Sentinel流控规则
Sentinel流控规则 流控规则基本介绍 名词解释 资源名:唯一名称,默认请求路径 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源) 阈值类型/单机阈 ...
- 【sentinel】流控规则详解
流量控制规则,简称流控规则,会对资源的流量进行限制.同一个资源可以对应多条限流规则.Sentinel会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕. 限流的直接表现是抛出Fl ...
最新文章
- 五子棋博弈树剪枝c语言,五子棋AI博弈树之带Alpha-Beta剪枝的极大极小过程函数...
- Kotlin的2017年总结与2018年展望
- Halcon算子:min_max_gray和gray_histo的区别
- linux密文解密工具,Linux之加密解密工具openssl的用法以及自建CA
- python图像质量评价_OpenCV图像质量评价的SSIM算法(图像相似度)
- 显示和隐藏菜单栏(两种方式div、table)
- RocketMq 消费消息的两种方式 pull 和 push
- iframe页面改动parent页面的隐藏input部件value值,不能触发change事件。
- 检查硬件变化的命令kudzu
- JS中移动端项目取余数和switch于PC端的不同
- SSO单点登录之同域登录的实现
- 鹏芯U盘(UDK2008)意外断电后修复 1
- AVR PIC单片机视频教程
- C++ 强连通分量 - 缩点(Tarjan算法)
- 《UnityAPI.Texture纹理》(Yanlz+Unity+SteamVR+云技术+5G+AI+VR云游戏+Texture+mipMapBias+wrapMode+立钻哥哥++OK++)
- python基于qq邮箱群发邮件
- 互联网,大数据和人工智能对我们的生活带来的影响
- 微信开发者模式php,php 开启微信公众号开发者模式
- 加密狗圣天诺LDK V7.5特性
- 光纤接入设备及使用图解
热门文章
- Copilot免费时代结束!正式版67元/月,学生党和热门开源项目维护者可白嫖
- 单网口电脑安装openwrt软路由做单臂路由
- JS读取Cookie
- 中国网民总结十大最反感词句(copy)
- Adobe CS5全套软件官方下载地址(简体中文完整版)
- Hive列转行 (Lateral View + explode)详解
- aix ntp 配置_IBM AIX系统NTP配置方法
- Python3 日期时间 相关模块(time(时间) / datatime(日期时间) / calendar(日历))
- Vulkan 预旋转处理设备方向
- 【杂记】(富文本框回填值、ajax数据回填按钮年级学科、去除数组中数组外包的引号、多重循环的写法、微测评获奖页面的内容 循环拼接写法、textarea禁止拖动、html基本、透明度、页面内出现滚动条)