2.3 令牌桶算法:
①. 某种意义上讲,令牌桶算法是对漏桶算法的一种改进:
a. 桶算法能够限制请求调用的速率.
b. 令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用.
②. 在令牌桶算法中,存在一个桶,用来存放固定数量的令牌.
a. 算法中存在一种机制,以一定的速率往桶中放令牌.
b. 每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行.
c. 否则选择等待可用的令牌、或者直接拒绝.
③. 放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌.所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行.
④. 比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了.这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求.
⑤. 所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行.
2.3.1 令牌桶实现原理:
对于令牌桶中令牌的产生一般有两种做法:
(1). 开启一个定时任务,由定时任务持续生成令牌:
①. 会极大的消耗系统资源.
②. 如某接口需要分别对每个用户做访问频率限制:
假设系统中存在6W用户,则至多需要开启6W个定时任务来维持每个桶中的令牌数,这样的开销是巨大的.
(2) 延迟计算:
定义一个resync函数,该函数会在每次获取令牌之前调用.实现思路:
①. 若当前时间晚于nextFreeTicketMicros,则计算该段时间内可以生成多少令牌,将生成的令牌加入令牌桶中并更新数据.这样一来,只需要在获取令牌时计算一次即可.
②. Swoft采用的是第二种,当每次获取令牌时,先执行resync来更新令牌桶中令牌的数量,从而达到异步产生令牌的目的.
3. 在swoft-limiter组件进行开发:
超出最大请求数,会报一个异常.
Swoft限流器底层采用的是令牌桶算法,底层依赖于Redis实现分布式限流.
4. 手工执行限流函数(非注解):
(1). 在RPC服务中结合consul在服务中配置限流参数:
app\Listener\RegisterServiceListener.php:
public function handle(EventInterface $event): void
{
$service = [
'Tags' => [
'order_rpc',
'gw.NAMESPACE=App.Rpc.Lib',
// 增加限流参数
'gw.rateLimiter.key=order', // 限流key(要求唯一)
'gw.rateLimiter.rate=1', // 最大的请求数
'gw.rateLimiter.max=1' // 允许多大的请求访问,请求数/秒
],
...
];
...
}