服务限流作为分布式系统中保证系统稳定性的一个重要的手段,在我们的日常设计和开发中经常使用到。令牌桶算法是一种非常常用的限流算法,下图就描绘了令牌桶算法的基本过程。




下载限速令牌桶 java python令牌桶限流_限流


  1. 处理请求前先到令牌桶中获取一个令牌
  2. 如果桶中没有令牌就表示需要限流,可以根据策略选择直接拒绝请求或者挂起一段时间。
  3. 如果拿到了令牌,表示不需要限流,放行请求,同时令牌桶中的令牌数要进行相应的扣减。
  4. 同时有一个独立的令牌生产器按照固定的速率网桶中添加令牌。

实现一个简易的令牌桶算法

首先定义一个令牌桶


下载限速令牌桶 java python令牌桶限流_权重_02


然后通过线程池实现一个固定速率的令牌发生器。


下载限速令牌桶 java python令牌桶限流_权重_03


最后实现获取令牌的方法


下载限速令牌桶 java python令牌桶限流_限流_04


至此就完成了一个简单的基于令牌桶算法的限流器了,其实真的非常简单,就三个部分:1、令牌桶,2、令牌发生器,3、令牌获取接口。

接下来我们看看这个限流器可以如何使用。


下载限速令牌桶 java python令牌桶限流_限流_05


当然在实际使用的时候可以通过aop或者filter的方式对一批接口进行统一处理

Guava中是如何使用令牌桶算法进行限流的

上面基于令牌桶算法实现的限流器是非常简单的设计,里面有很多细节可以进行优化。

  • 使用到了线程池,显然不是一个高效的做法。
  • 不管什么请求,以及当前负载情况,处理方式都是固定的,不够灵活。
  • 所有的请求都是请求一个令牌,无法区分各个请求的限流权重。

接下来我们就分析一下Guava的限流器是如何优化这些细节的。

Guava中有两种限流器,SmoothBursty和SmoothWarmingUp,二者大部分内容都是一样的,只是在处理令牌发放速率算法上有一点区别。

Guava限流器的令牌生产机制

在Guava中令牌的生产过程是非常简易也是非常巧妙的,Guava只有在需要令牌的时候才会尝试去生产令牌。由于产生令牌的速率是固定的(每秒qps个),那么只需要知道上一次生产令牌的时间,就能够知道当前需要再生产多少个令牌了,从而大大的优化了上面demo中的令牌生产过程。实现代码如下图:


下载限速令牌桶 java python令牌桶限流_令牌桶算法和漏桶算法python_06


Guava限流器的令牌获取机制

demo中的简易限流器每次只能拿一个令牌,这就导致限流器不够灵活。而Guava中的限流器可以传入一个需要获取的令牌数,业务可以根据实际情况出入,起到灵活区别每一个请求的限流权重的目的。

上面的demo实现的简易限流器使用的是先从桶中拿令牌,如果没有的话就不停的尝试一小段时间的方式来获取令牌的,这种方式会导致发生限流的时候所有的请求都会不停的重试,效率会比较低。其实还有另外一种更加高效的方式--赊账模式:先放行请求,然后扣除令牌,如果令牌不够了后续的请求就会停止发放令牌,直到重新生产了足够多的令牌。

Guava两种限流器的区别

两种限流器最大的区别就是生产令牌的机制有点区别。

SmoothBursty限流器是一种非常简单的限流器,是按照匀速生产令牌的,并没有什么特别。

SmoothWarmingUp是带有预热机制的限流器。在实际生产过程中会存在一种情况,当一个服务启动或者长时间没有使用的时候,qps能力会低很多,而在请求较多的时候qps能力会高很多(比如请求较多的时候,缓存命中率可能会比较高),SmoothWarmingUp限流器就是专门用来应对这种情况的。这个限流器会在低负载的时候按照较慢的速率生成令牌,在高负载的时候按照较快的速率生成令牌,如下图所示:


下载限速令牌桶 java python令牌桶限流_限流_07


Guava限流器如何使用

首先我们看看如何创建Guava限流器:


下载限速令牌桶 java python令牌桶限流_权重_08


然后是获取令牌:


下载限速令牌桶 java python令牌桶限流_限流_09