限流篇,欣赏 guava 的 RateLimiter
为什么令牌桶实现不用生产-消费模式
将令牌桶的原理翻译成代码,一个生产令牌的线程 sleep 几秒,然后放到 ConcurrentQueue 一个令牌,业务线程取令牌的时候去 offer,为什么不这样做?
原因是限流发生在系统承受的极限上,这种时候限流的代码越稳定、越简单,效果就越好,生产令牌的线程可能跑飞、jvm 可能有 bug、可能不会被调度,总不能再加一个线程组吧,那怎么办?
假设每秒产生 1 个令牌,产生 n 的令牌,就需要 n 秒,只要有时间差,用时间差 / 速率,就能得到有多少令牌,那么时间差怎么来的?
当线程请求的这一刻,这个时间是确定的,记为 now,有令牌出现的时间,记为 last,(now-last) / rate 就等于当前有多少令牌了
为什么 RateLimiter 类中没有变量、计算
看一下 Ratelimit 类的方法,create 初始化、doSetRate 设置速率、acquire 获取令牌... 以 acquire 为例,我们要干嘛呢,如果有令牌就返回,如果没有就等待,等待时间可以计算出来
那么逻辑主要在获取等待时间里,进去是加锁、返回等待时间,再进去 max(time - now, 0),后面是一个 abstract 方法,这个方法就是实现类自己的实现策略了
也就是说,外面看到的 RateLimiter,只有做了什么,而没有怎么做,涉及的概念:速率、等待时间、令牌,简单点看就是上文中计算公式的主要变量,但这个变量怎么来的、怎么计算、怎么存储,Ratelimit 类里面是没有的
这么做依据什么思想呢,开闭?面向接口?不如多看点代码、多想想,念叨名词是没用的
为什么叫 SmoothRateLimiter 平滑实现类
到了实现类 SmoothRateLimiter,平滑限流,那意思是线性的呗,实现的变量都在了,当前存储的令牌数 storedPermits,最大存储的令牌数 maxPermits,单位生产间隔时间 stableIntervalMicros,下次令牌出现时间 nextFreeTicketMicros
下面是获取等待时间的注释版代码,看一眼就可以
我们可以看到,有两种实现类,一个是 SmoothBursty,平滑稳定版,另一个是 SmoothWarmingUp,平滑慢热版,区别从这个注释可以看出来,主要的实现方法是三个,coolDownIntervalMicros 单位冷却时间能产生的令牌数,storedPermitsToWaitTime 动态调整等待时间,doSetRate 设置生产速率
有没有感觉到,不论是类、方法、变量命名,方法分层、注释都挺明了的,想想你写的 IF {IF {FOR}} ELSE{ WHILE{}},你可长点心吧
为什么 SmoothBursty、SmoothWarmingUp 是内部静态类
没有明显的结论,把 SmoothRateLimiter 当成一个轮廓,平滑的实现都在这里面?但是也没别的实现了呀,有必要这么做吗
结束语,欢迎各位批评
版权声明: 本文为 InfoQ 作者【下雨喽】的原创文章。
原文链接:【http://xie.infoq.cn/article/8d98a05a3b7eb9c2bd6bc3f07】。文章转载请联系作者。
评论