令牌桶算法

x33g5p2x  于2022-01-11 转载在 其他  
字(1.8k)|赞(0)|评价(0)|浏览(345)

一 算法

令牌桶算法和漏桶算法不同的是,有时后端能够处理一定的突发情况,只是为了系统稳定,一般不会让请求超过正常情况的60%,给容灾留有余地。但漏桶算法中后端处理速度是固定的,对于短时的突发情况,后端不能及时处理,和实际处理能力不匹配。

令牌算法是以固定速度往一个桶内增加令牌,当桶内令牌满了后,就停止增加令牌。上游请求时,先从桶里拿一个令牌,后端只服务有令牌的请求,所以后端处理速度不一定是匀速的。当有突发请求过来时,如果令牌桶是满的,则会瞬间消耗桶中存量的令牌。如果令牌还不够,那么再等待发放令牌(固定速度),这样就导致处理请求的速度超过发放令牌的速度。

如下图,灰色部分是令牌桶,有容量限制,只能最多存放 capacity 个令牌,每秒以固定的速度向桶中增加令牌,如果桶的容量满了,则等待桶中令牌被消耗后,再增加令牌。另一边应用进程拿到令牌后才处理请求,如果没有拿到令牌,则不处理该请求。

1 有一个固定容量的桶存放令牌(Token)。

2 桶初始化是空的,以固定的速度(rate)向桶里填充 Token,当达到桶的容量时,多余的令牌被丢弃。

3 当请求到来时,从桶里移除一个令牌,如果没有令牌,则拒绝该请求。

令牌桶控制的是令牌入桶的速度,对于拿到令牌的速度没有限制,允许一定的突发流量被瞬间处理。

二 需求

1 设计一个令牌桶,能放 2000 个令牌,放令牌的速度是每毫秒1个令牌。即1秒放1000个令牌。

2 外部最大请求为 1500 个,并随机取值,会限流吗?

三 代码

  1. package currentLimit;
  2. import java.util.Random;
  3. /**
  4. * @className: TokenBucket
  5. * @description: 令牌桶算法
  6. * @date: 2022/1/8
  7. * @author: cakin
  8. */
  9. public class TokenBucket {
  10. static long cur = 0; // 当前桶中令牌数量
  11. // 记录上次统计时间的毫秒
  12. static long lastTime = System.currentTimeMillis();
  13. /**
  14. * 功能描述:漏桶算法
  15. *
  16. * @param capcity 桶能承载的最大令牌数
  17. * @param rate 放入令牌的速度,系统每毫秒放入的令牌数
  18. * @return true:限流 false:不限流
  19. * @author cakin
  20. * @date 2022/1/9
  21. */
  22. static boolean TokenBucket(int capcity, int rate) {
  23. // 当前毫秒
  24. long millisTime = System.currentTimeMillis();
  25. // 两次请求的间隔,单位是毫秒
  26. long time = (millisTime - lastTime);
  27. // 当前桶中的令牌数
  28. cur = Math.min(capcity, cur + time * rate);
  29. lastTime = millisTime;
  30. if (cur == 0) {
  31. // 没有令牌拿
  32. return true;
  33. }
  34. // 拿走一块令牌
  35. --cur;
  36. return false;
  37. }
  38. // 测试方法
  39. public static void main(String[] args) {
  40. for (; ; ) {
  41. // 停顿1秒
  42. try {
  43. Thread.sleep(1000);
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. // 模拟1秒请求的次数 1500次内
  48. int randomTime = new Random().nextInt(1500); // 通过调这个值,来测试是否限流
  49. for (int j = 0; j < randomTime; j++) {
  50. request();
  51. }
  52. }
  53. }
  54. /**
  55. * 功能描述:模拟请求
  56. *
  57. * @author cakin
  58. * @date 2022/1/9
  59. */
  60. private synchronized static void request() {
  61. if (TokenBucket(2000, 1)) {
  62. System.out.println("限流了" + cur);
  63. } else {
  64. System.out.println("没限流" + cur);
  65. }
  66. }
  67. }

四 测试

五 说明

从测试来看,当外部请求的的请求数在1500内随机取值,还是可能限流的。

计数器算法、滑动窗口算法、漏桶算法、令牌桶算法,这几种算法的功能逐渐增强,但实现的难度也逐渐增大。由于过载保护是一个通用的功能,一般都在框架底层实现,所以采用令牌桶算法是较好的选择之一。实现一次,可以在很多模块上复用。

相关文章