Sentienl 流控效果之匀速排队与预热实现原理与实战建议

本文详细解释了Guava框架中WarmUpController类的构造函数和canPass方法,涉及冷却因子、预热时间、预热算法原理,以及syncToken和coolDownTokens方法的实现。重点在于理解冷却因子如何影响限流策略的动态调整。
摘要由CSDN通过智能技术生成

冷却因子。

  • 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) { // @1

if (coldFactor <= 1) {

throw new IllegalArgumentException(“Cold factor should be larger than 1”);

}

this.count = count;

this.coldFactor = coldFactor;

warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1); // @2

maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor)); // @3

slope = (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(); // @1

long previousQps = (long) node.previousPassQps(); // @2

syncToken(previousQps); // @3

// 开始计算它的斜率

// 如果进入了警戒线,开始调整他的qps

long restToken = storedTokens.get();

if (restToken >= warningToken) { // @4

long aboveToken = restToken - warningToken;

// 消耗的速度要比warning快,但是要比慢

// current interval = restToken*slope+1/count

double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));

if (passQps + acquireCount <= warningQps) {

return true;

}

} else { // @5

if (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; // @1

long oldLastFillTime = lastFilledTime.get();

if (currentTime <= oldLastFillTime) { // @2

return;

}

long oldValue = storedTokens.get();

long newValue = coolDownTokens(currentTime, passQps); // @3

if (storedTokens.compareAndSet(oldValue, newValue)) {

long currentValue = storedTokens.addAndGet(0 - passQps); // @4

if (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) { // @1

newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);

} else if (oldValue > warningToken) { // @2

if (passQps < (int)count / coldFactor) {

newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);

}

}

return Math.min(newValue, maxToken);// @3

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

权威指南-第一本Docker书

引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。

总共包含了:简介、安装Docker、Docker入门、使用Docker镜像和仓库、在测试中使用Docker、使用Docker构建服务、使用Fig编配Docke、使用Docker API、获得帮助和对Docker进行改进等9个章节的知识。

image

image

image

image

关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
链图片转存中…(img-yaq4o77v-1712067194337)]

[外链图片转存中…(img-Z3saUMYr-1712067194338)]

[外链图片转存中…(img-reJJoGmw-1712067194338)]

关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值