[关闭]
@zero1036 2019-03-22T11:52:25.000000Z 字数 1808 阅读 1957

限流算法-Guava RateLimiter

Java-其他库 架构


TG架构笔记


漏桶算法 vs 令牌桶算法

漏桶算法(Leaky Bucket):水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.示意图如下:

05DCF4D8-628D-4B27-AB47-123F8A4C2D67.png-123.9kB

令牌桶算法(Token Bucket):与 Leaky Bucket具有相同效果但反方向算法,系统以固定速度派发token,请求取走token后执行实际操作,当没有可领取token时,请求阻塞或拒绝,示意图如下:

054109C2-9398-45E8-9BC4-7239DBF2B5A0.png-142.9kB

两者比较下的特点:

  1. 令牌桶:增加令牌速率恒定;漏桶:消费令牌恒定
  2. 由于第1点,令牌桶允许一定程度的突发,而漏桶将所有的突发均平滑处理了
  3. 两者算法实现可以是一样的,对于相同的参数得到的限流效果是一样的

49DD4072-03EF-40BA-A869-261BD7568917.png-97.3kB

Guava RateLimiter

api

limiter.acquire(i):以阻塞的方式获取令牌

tryAcquire(int permits, long timeout, TimeUnit unit):设置等待超时时间的方式获取令牌,如果超timeout为0,则代表非阻塞,获取不到立即返回

getRate():返回RateLimiter 配置中的稳定速率,该速率单位是每秒多少令牌数

setRate(double permitsPerSecond):更新RateLimite的稳定速率,参数permitsPerSecond 由构造RateLimiter的工厂方法提供

平滑突然限流 SmoothBursty

  1. /**
  2. * 平滑突发限流:SmoothBursty
  3. */
  4. @Test
  5. public void test1() {
  6. //每秒允许5个请求,表示桶容量为5且每秒新增5个令牌,即每隔0.2秒新增一个令牌
  7. RateLimiter limiter = RateLimiter.create(5);
  8. /**
  9. * 一次性消费5个令牌,改变一次性的令牌数,会影响下一个acquire的等待时间
  10. * output : 0.0
  11. */
  12. System.out.println(limiter.acquire(5));
  13. /**
  14. * limiter.acquire(1)将等待差不多1秒桶中才能有第一个令牌
  15. * output: 0.995618
  16. */
  17. System.out.println(limiter.acquire(1));
  18. /**
  19. * 0.2秒后派发新令牌
  20. * output:0.2
  21. */
  22. System.out.println(limiter.acquire(1));
  23. /**
  24. * 0.2秒后派发新令牌
  25. * output:0.2
  26. */
  27. System.out.println(limiter.acquire(1));
  28. /**
  29. * 0.2秒后派发新令牌
  30. * output:0.2
  31. */
  32. System.out.println(limiter.acquire(1));
  33. }

原理

恒定速率向令牌桶增加令牌的2种实现思路:
1、通过定时器实现,缺点:成本高
2、通过计录增加令牌的时刻,并计算请求执行的当前时间之差,获得令牌结果。类似Guava LoadingCache的缓存过期设计。

关键操作resync(long nowMicros): 通过计录增加令牌的时刻,并计算请求执行的当前时间之差,获得令牌结果

  1. nextFreeTicketMicros:最近一个新增令牌到桶的时刻
  2. maxPermits:最大令牌数
  3. storedPermits:有效令牌书数

code:

  1. void resync(long nowMicros) {
  2. // if nextFreeTicket is in the past, resync to now
  3. // 当前【最近一个新增令牌的时刻】已过去,则重新同步计算【nextFreeTicketMicros】与【storedPermits】
  4. if (nowMicros > nextFreeTicketMicros) {
  5. // 计算时间差内,可以新增的令牌数
  6. double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
  7. // 存量加增量令牌数 与 令牌桶总量 取较小者作为存量令牌数
  8. storedPermits = min(maxPermits, storedPermits + newPermits);
  9. // 当前时间设为 【最近一个新增令牌的时刻】
  10. nextFreeTicketMicros = nowMicros;
  11. }
  12. }

关键参数

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注