填充令牌的逻辑如下:
- 拿到当前的时间,然后去掉毫秒数,得到的就是秒级时间
- 判断时间小于这里就是为了控制每秒丢一次令牌
- 然后就是coolDownTokens去计算我们的冷启动/预热是怎么计算填充令牌的
- 后面计算当前剩下的令牌数这个就不说了,减去上一次消耗的就是桶里剩下的令牌
- 最开始的事实因为lastFilledTime和oldValue都是0,所以根据当前时间戳会得到一个非常大的数字,最后和maxToken取小的话就得到了最大的令牌数,所以第一次初始化的时候就会生成maxToken的令牌
- 之后我们假设系统的QPS一开始很低,然后突然飙高。所以开始的时候回一直走到高于警戒线的逻辑里去,然后passQps又很低,所以会一直处于把令牌桶填满的状态(currentTime - lastFilledTime.get()会一直都是1000,也就是1秒),所以每次都会填充最大QPScount数量的令牌
- 然后突增流量来了,QPS瞬间很高,慢慢地令牌数量就会消耗到警戒线之下,走到我们if的逻辑里去,然后去按照count数量增加令牌
上面的逻辑理顺之后,我们就可以继续看限流的部分逻辑:
- 令牌计算的逻辑完成,然后判断是不是超过警戒线,按照上面的说法,低QPS的状态肯定是一直超过的,所以会根据斜率来计算出一个warningQps,因为我们处于冷启动的状态,所以这个阶段就是要根据斜率来计算出一个QPS数量,让流量慢慢地达到系统能承受的峰值。举个例子,如果count是100,那么在QPS很低的情况下,令牌桶一直处于满状态,但是系统会控制QPS,实际通过的QPS就是warningQps,根据算法可能只有10或者20(怎么算的不影响理解)。QPS主键提高的时候,aboveToken再逐渐变小,整个warningQps就在逐渐变大,直到走到警戒线之下,到了else逻辑里。
- 流量突增的情况,就是else逻辑里低于警戒线的情况,我们令牌桶在不停地根据count去增加令牌,这时候消耗令牌的速度超过我们生成令牌的速度,可能就会导致一直处于警戒线之下,这时候判断当然就需要根据最高QPS去判断限流了。
所以,按照低QPS到突增高QPS的流程,来想象一下这个过程:
- 刚开始,系统的QPS非常低,初始化我们就直接把令牌桶塞满了
- 然后这个低QPS的状态持续了一段时间,因为我们一直会填充最大QPS数量的令牌(因为取最小值,所以其实桶里令牌基本不会有变化),所以令牌桶一直处于满的状态,整个系统的限流也处于一个比较低的水平
这以上的部分一直处于警戒线之上,实际上就是叫做冷启动/预热的过程。
- 接着系统的QPS突然激增,令牌消耗速度太快,就算我们每次增加最大QPS数量的令牌任然无法维持消耗,所以桶里的令牌在不断低减少,这个时候,冷启动阶段的限制QPS也在不断地提高,最后直到桶里的令牌低于警戒线
- 低于警戒线之后,系统就会按照最高QPS去限流,这个过程就是系统在逐渐达到最高限流的过程
那这样一来,实际就达到了我们处理突增流量的目的,整个系统在漫漫地适应突然飙高的QPS,然后最终达到系统的QPS阈值。
- 最后,如果QPS回复正常,那么又会逐渐回到警戒线之上,就回到了最开始的过程。

总结
因为算法如果单独说的话都比较简单,一说大家都可以听明白,不需要几个字就能说明白,所以还是得弄点源码看看别人是怎么玩的,所以尽管我很讨厌放源码,但是还是不得不干。
光靠别人说一点其实有点看不明白,按照顺序读一遍的话心里就有数了。
那源码的话最难以理解的就是令牌桶的实现了,说实话那几个计算的逻辑我看了好几遍不知道他算的什么鬼,但是思想我们理解就行了,其他的逻辑相对来说就比较容易理解。
文章转自公众号:艾小仙