限流

限流分为:合法性验证限流、容器限流、服务端限流等几类。

合法性验证限流

验证码,IP 黑名单

容器限流

  • tomcat 限流:配置最大线程数
  • nginx 限流:控制速率或控制并发连接数

服务端限流(限流器)

滑动窗口算法

滑动时间算法指的是以当前时间为截止时间,往前取一定的时间,比如往前取 60s 的时间,在这 60s 之内运行最大的访问数为 100。此时算法的执行逻辑为,先清除 60s 之前的所有请求记录,再计算当前集合内请求数量是否大于设定的最大请求数 100,如果大于则执行限流拒绝策略,否则插入本次请求记录并返回可以正常执行的标识给客户端。

可以借助 Redis 的有序集合 ZSet 来实现时间窗口算法限流。实现方式是先使用 ZSet 的 key 存储限流的 ID,score 用来存储请求的时间。每当有请求访问,先清空之前时间窗口的访问量,统计现在时间窗口的请求个数,和最大允许访问量对比。如果大于等于最大访问量则返回 false 执行限流操作,否则允许执行业务逻辑,并在 ZSet 中添加一条访问记录,

漏桶算法

先声明一个队列用来保存请求,这个队列相当于漏斗,当队列容量满了之后就放弃新来的请求,然后重新声明一个线程定期从任务队列中获取一个或多个任务进行执行,这样就实现了漏桶算法。

令牌桶算法

以某种恒定的速度生成令牌,并存入令牌桶中,而每个请求需要先获取令牌才能执行,如果没有获取到令牌的请求可以选择等待或者放弃执行。

Google 开源的 guava 包,有令牌桶算法的实现:

import com.google.common.util.concurrent.RateLimiter; 
import java.time.Instant; 
 
/** * Guava 实现限流 */ 
public class RateLimiterExample { 
    public static void main(String[] args) { 
        // 每秒产生 10 个令牌(每 100 ms 产生一个) 
        RateLimiter rt = RateLimiter.create(10); 
        for (int i = 0; i < 11; i++) { 
            new Thread(() -> { 
                // 获取 1 个令牌 
                rt.acquire(); 
                System.out.println("正常执行方法,ts:" 
                    + Instant.now()); 
            }).start(); 
        } 
    } 
}

分布式限流

可以使用 redis-cell模块实现,本质是令牌桶算法。

参考资料

人人都能看懂的 6 种限流实现方案