10年 Java程序员,硬核人生!勇往直前,永不退缩!

欢迎围观我的git:https://github.com/R1310328554/spring_security_learn 寻找志同道合的有志于研究技术的朋友,关注本人微信公众号: 觉醒的码农,或Q群 165874185

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

什么是平滑限流?它相对于固定、滑动窗口限流,它可以提供某种平滑流量的功能。RateLimiter本意是 速率限制器,而它的2个实现都是平滑的!RateLimiter 有2个实现是 SmoothBursty和SmoothWarmingUp,两个实现都是Smooth开头,表明了其平滑的特性。所以,可以认为RateLimiter是平滑限流器!

SmoothBursty其实很简单,也很好理解,它就是一个没有上限的令牌桶, 因为没有上限,所以允许突发的流量, 不管多少,都能够接受!(当然,其实也是有上限的,是Integer.MAX_VALUE)。但是SmoothWarmingUp的理解难度陡然上升。看到一些文章说,SmoothWarmingUp 看起来是令牌桶, 也有说是漏铜,是吗? 据我观察,都不准确,如果非要选一个的话,那还是漏桶,因为它本质上是令牌桶上做了修改。SmoothWarmingUp的使用也不难,基本的理解也不难,难的是源码。我从guava 16的版本的源码看到如下的图:

 老实说,这个图真难理解。后面我从最新的30.1-jre  版本看到这样的图:

其实现原理大同小异,但是显然 最新的版本命名、设计更加合理, 所以我后面使用最新版本来做分析。最新版的限流器的名字也有一些变化:

WarmingUp -> SmoothWarmingUp

Bursty   -> SmoothBursty

halfPermits -> thresholdPermits

可以发现命名更加合理。当然这个也不很重要。

 

关键字段

下面单独分析 SmoothWarmingUp,即平滑预热限流器。下面对源码中限流器的关键字段或属性做简单介绍,父类提供的字段不难理解:

permitsPerSecond 稳定状态下,每一秒产生多少个许可?

warmupPeriodMicros 预热时间,单位是微秒

 

但下面的字段很难理解:

maxPermits 最大可以预存的许可。 这里我没有直译,而是使用了预存,当然,你可以理解为预取。 本意是从完全非饱和(冷却)状态到完全饱和(刚好完成预热)状态 的过程可以产生的许可。这个也很不好理解。但是非常关键。其实就是从冷却到刚好完成预热的时间内应该产生的许可。后面我们会发现,它是一个固定的值。为什么是固定? 其实是通过一系列假设,然后计算出来的。后面详述。对于SmoothBursty来说,这个变量没有意义。

storedPermits  实际预存的许可。这是一个动态变化的值,变化范围就是上面那个图,也就是0 - maxPermits ,你可以理解为预取。 本意是从限流器当前时间所处的状态,到刚好完成预热稳定运行的时间内应该产生的许可。如果当前限流器已经稳定运行,那么固定就是0;为什么要维护这样一个值? 这个一开始真不好理解。 谁说限流器里面必须一个已存储的许可字段? 它的实际意义是? 说不清它的实际意义,因为太抽象。 可以理解为预热限流器内部维护的一个状态,表明的冷热程度。对于SmoothBursty来说,这个变量没有意义。已存储的许可? 其实有很大的误导性,它并不是对之前未利用的许可的缓存或者存储起来了,而是 表明一种未利用的状态,或者说一种冷热状态!

stableIntervalMicros 稳定状态下,每产生一个许可需要需要消耗的时间。其实说时间间隔 好像更准确,因为它只有在那种连续使用的状态下才能保持稳定。它单位是微秒。stableIntervalMicros  其实就是速度的倒数。间隔越大,当然速度越低。反之亦反。我们把它简称为间隔。比如说速率即permitsPerSecond 是5个/s,那么稳定状态产生一个许可的时间是 1/ permitsPerSecond  = 0.2s

slope 斜率,也就是预热的增速

thresholdPermits 许可门槛。限流器处于刚好完成预热的时间点的时候,这个时间点上,速度刚刚好是stableIntervalMicros,如果闲置,那么立即又会开始冷却,也就是速度变小, 它是一个临界点。这个非常难理解。理解它表示一直临界状态非常关键。同时需要注意,为什么我们已经刚好完成了预热,还不是0?  如果是0, 似乎更合理,但实际上,它就不是。

coldFactor 冷却因子。用于计算从完全冷却的状态,启动限流器,限流器的起始速度。我开始认为预热的最开始的速度,应该是0, 这个是非常错误的理解,导致我后面花了很多时间去纠正。 其实不是的,一开始就有速度了,不过是比较慢(因为是冷却状态)

 

三个状态&三个阶段

我们要区分,稳定运行、完全预热、预热完成、刚好结束/完成预热, 稳定运行或完全预热是指storedPermits 为0 的时候,预热完成、刚好结束/完成预热是指 速率刚好达到稳定状态的时候。 两者还是不同的。 理解这点非常重要。

要想理解其原理,还有一点需要特别注意,平滑预热限流器有一个冷却的过程,这个过程和预热是反向的。理解这个过程非常的关键。经过我反复的研究,发现其实这样的过程: 

 

冷却状态:storedPermits = maxPermits 的时候

预热过程:间隔从coldFactor * stableIntervalMicros  逐步的、稳定的减少到 stableIntervalMicros,同时storedPermits 从maxPermits减少到 thresholdPermits  的过程 。耗时是warmupPeriodMicros。warmupPeriodMicros 是参数,是固定的,创建的时候就固定了,不能改,除非重建。

临界状态:storedPermits = thresholdPermits 的时候

准稳定阶段:其实我一开始是把storedPermits 从maxPermits减少到0的过程, 当做了预热,好像也没啥大错,但是这样就不好理解源码了! 而 storedPermits 从thresholdPermits 减少到0 的过程, 是一个稳定的过程,其实我们也可以给与一个命名: 准稳定过程, 我觉得这样的命名是有意义的。。为什么冷却过程的间隔保持不变为 stableIntervalMicros? 为什么不是从stableIntervalMicros 到 coldFactor * stableIntervalMicros  ?一开始,我也认为是应该是一个逐步冷却的过程,虽然这样符合一个人的直观感受,比如 冷却肯定也是 非常热,然后温度慢慢下降吧, 比如开始的冷却过程,烙铁的冷却等等。不好意思,RateLimiter 的冷却就是戛然而止, 就是固定的时长的闲置,不减速的过程,然后戛然而止的速度降为0 (好像也不是0, 而是coldFactor * stableIntervalMicros ...),然后就认为是完全冷却了下来。完全冷却了下来,当然,就会有任何状态的变化了,又相当于回到了 限流器刚创建的时候的样子! 

稳定状态:storedPermits = 0的时候

冷却阶段:storedPermits 从0增加到maxPermits 的过程。因为间隔保持为 stableIntervalMicros ,耗时和冷却时间和预热时间完全一样,同样是warmupPeriodMicros。何以确定?两个都是个人为设置的,是为了简化计算。 其实完全可以是不同的。如果相同,所以我们可以计算冷却的间隔:maxPermits / warmupPeriod,为什么?从而stableIntervalMicros  = maxPermits / warmupPeriod,从而 maxPermits = warmupPeriod * stableIntervalMicros  。

 

非人类的设计啊! 难怪让人很久理解不了!但 这一点思维必须改变过来。本质上说,WarmingUp还是令牌桶,也允许突发,但是每次启动需要预热,即warm/ramp up,然后闲下来之后,它会冷却。冷却的过程,好像也可以理解为漏桶..  不过当然不是真正意义的漏桶。

 

理解了前面的各种说明,就不难理解官方的图了。图的横轴是 存储的许可。纵轴是限流器的速率状态,准确说是限流器每产生一个许可的时间。 这两个指标怎么会有如此密切的关联? 而且是如此简单的折线关系? 刚开始的时候,非常懵,后面才明白,其实也是蛮合理的,一切都是为了保证 预热限流器的预热特效,为了使这个特性完全实现, 也是想尽办法。

 

首先,坐标系原点到底是什么东西? 确实也不好理解。按照上面的分析,原点其实就是稳定运行时候的限流器的状态。但对于预热过程,它其实没有意义。对于冷却过程,它是冷却过程的起点! 理解这点非常重要。 虽然是两个相反的过程,但是也完全有机的整合到了一个坐标系。我想如果分开为下面的两个图, 是不是更加好理解呢?

 

 

 

 

 

 

 

 实际中,两个过程除了有一个共同的起点、终点之后,其实没其他关系了。所以说,实际关系不大,但我们可以做一些假设,以简化问题。

坐标系的面积是什么?是时间! 一般来说,时间只是一个坐标,但是这里做了坐标的变换,这里面积竟然是时间! 千万要注意!

 

计算和求解 

随便在横轴的一个点(不能超出0 - max),那么限流器所处的状态就是半冷半热状态!如果我们画一条竖线,那么左边的面积是到刚好完成预热 并且到稳定状态的时间。而右边的就是它到完全冷却需要的时间。注意到 冷却的时间是 一个矩形。而预热+准稳定的时间是 一个 不规则四边形! 为什么不规则啊!其实官方的图有很大的误导性,而且刚好 冷却速度和稳定的速度是一样的,所以重叠为一个不规则四边形。如果分开两个图, 会更加好理解!对于下图任意一点,其实都是处于半冷半热的状态,如果继续冷却(即闲置),那么往右走,最多到maxPermits位置,如果重新使用(即调用acquire方法),那么往左走,最多走到原点。往左走,获取N个permit,那么可能是下图所示,可以累计计算绿色和红色部分的面积 作为时间!

 

再次说明,storedPermits 是目前为止,过去的未利用的资源; 冷却之后再次使用,是否还需要预热?是应该更慢还是更快? 这个其实没有唯一答案,还是得看具体场景; 启动时候的初始预热和冷却之后再次使用,其实还是不同的,所以; 其实实际的工业应用中,情况可能更加复杂。那就可能不是简单一条折线能够描述的了! an unused RateLimiter 实际表示限流器的下一个可以实际获取的许可 是之前的时间。 acquire(100) 难道需要等100s? 显然非常不合理。实际的做法都是,直接开始,然后把后续的请求延迟,延迟到acquire(100) 实际结束的时间点。 acquire(3) is equivalent to { acquire(1); acquire(1); acquire(1); }, or { acquire(2); acquire(1); }, etc, since the integral of the function in [7.0, 10.0] is equivalent to the sum of the integrals of [7.0, 8.0], [8.0, 9.0], [9.0, 10.0] (and so on),  问题是,maxPermits的含义是? 其实maxPermits 跟预热没关系,它表明的是 完全稳定到完全冷却之间可未被利用的 许可。是缓存起来的许可。 因为 冷却时间是固定的,冷却速度也是确定的, 所以 maxPermits 就这样被计算出来了!冷却时间、 冷却速度 是两个假设,完全是人为的假设; thresholdPermits 是怎么计算的?  首先maxPermits 已经有了,然后冷却的面积因为涉及thresholdPermits,不可以直接计算,我们需要通过列方程计算:

假设

1 冷却速度固定,间隔为stableIntervalMicros

2 冷却时间 = 预热时间

那么有:

warmupPeriodMicros = coolDownPeriodMicros

warmupPeriodMicros = (maxPermits - thresholdPermits) / (stableIntervalMicros + coldFactor * stableIntervalMicros) / 2
coolDownPeriodMicros = stableIntervalMicros * maxPermits 

其中 warmupPeriodMicros、coldFactor 是参数,coldFactor固定是3;stableIntervalMicros 也可以由参数直接计算。故这是一个三元一次方程, 变量为coolDownPeriodMicros maxPermits thresholdPermits。 如果2个假设不满足,那么无法求解!

计算可得:
thresholdPermits = maxPermits - 2 * maxPermits / ( 1 + coldFactor)


令coldFactor = 3, 那么 thresholdPermits = maxPermits / 2 。这个也是默认设置。
令coldFactor = 1, 那么 thresholdPermits = 0; 也就是说, 如果预热也是平直的过程,那么。。
令coldFactor = 5, 那么 thresholdPermits = 2 * maxPermits / 3; 以此类推。。

总之,不管coldFactor 是多少, 完全稳定到完全冷却的图形中,消耗 maxPermits 是相同的, 它们右边的图形是部分重叠的。 如果maxPermits  不相同? 那么问题不好求解。

 

很累的总结

为什么从 冷却、半冷却状态回到 完全预热状态 需要走一条折线? 这个是最难理解的?为什么 预热结束后 还需要走一段横线。从完全冷却 预热到完全稳定, 需要消耗maxPermits , 花费时间是 warmupPeriodMicros  + stableIntervalMicros  * thresholdPermits; 而从完全稳定冷却到 完全冷却,需要的时间是warmupPeriodMicros , 两者并不一样。为什么不一样? 为什么不是一个逆过程? 感觉还是因为预热。 就是说 预热需要时间,速度比较慢,冷却就快一些!如果是这样, warmupPeriod = coolDownPeriod 还能够成立吗? 看你怎么理解 warmupPeriod , warmupPeriod其实是加速的过程,但是速度稳定之后, 并不是立即预热完毕。 比较拗口啊。

 

反反复复说了这么多,自己都觉得非常啰嗦了。总之 thresholdPermits 所处时间点,是个临界点。 理解上面各个关键字段和各种状态转变了之后,你就不会有那么多为什么了,然后源码也就不难理解了!

 

posted on 2021-03-13 22:51  CanntBelieve  阅读(1407)  评论(0编辑  收藏  举报