常用限流算法

1、计数器(固定窗口)算法

计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。
此算法在单机还是分布式环境下实现都非常简单,使用redis的incr原子自增性和线程安全即可轻松实现。
但该方法存在一个严重问题, 即假设1min内服务器的负载能力为100,因此一个周期的访问量限制在100,然而在第一个周期的最后5秒和下一个周期的开始5秒时间段内,分别涌入100的访问量,虽然没有超过每个周期的限制量,但是整体上10秒内已达到200的访问量,已远远超过服务器的负载能力,由此可见,计数器算法方式限流对于周期比较长的限流,存在很大的弊端。

2、漏斗算法(漏桶算法)

漏斗算法是访问请求到达时直接放入漏斗,如当前容量已达到上限(限流值),则进行丢弃(触发限流策略)。漏斗以固定的速率进行释放访问请求(即请求通过),直到漏斗为空。

3. 令牌桶算法

令牌桶算法可以说是对漏桶算法的改进。漏桶算法能限制请求的速率。而令牌桶算法在限制请求速率的同时还允许一定程度的突发调用
令牌桶算法是程序以r(r=时间周期/限流值)的速度向令牌桶中增加令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则通过请求,否则触发限流策略
技术实战篇 - 图1

使用Redis实现令牌桶 :
也就是说我们每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。
依靠上述的思想,我们可以结合Redis的List数据结构很轻易的做到
依靠List的leftPop来获取令牌 :

  1. // 输出令牌
  2. public Response limitFlow2(Long id){
  3. Object result = redisTemplate.opsForList().leftPop("limit_list");
  4. if(result == null){
  5. return Response.ok("当前令牌桶中无令牌");
  6. }
  7. return Response.ok(articleDescription2);
  8. }

再依靠Java的定时任务,定时往List中rightPush令牌 :

  1. // 10S的速率往令牌桶中添加UUID,只为保证唯一性
  2. @Scheduled(fixedDelay = 10_000,initialDelay = 0)
  3. public void setIntervalTimeTask(){
  4. redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());
  5. }