业务背景
一般做接口限流主要是为了应对突发流量,避免突发流量拖垮服务。如下面一些场景就有可能发生突发流量
- 微博热搜
- 恶意刷单
- 恶意爬虫
- 促销活动
令牌桶算法
我们用一个桶来盛放令牌,假设桶的上限为100个,令牌生成速度为10个/s。超过100个则丢弃多余的令牌。这样当接口请求突然激增时,前100个可以正常请求,后续则以每秒10个可以正常请求。
实现
用队列保存令牌,用ScheduledThreadPoolExecutor来定时放令牌
一般使用google提供的guava工具包即可
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
// 每秒生成2个令牌
RateLimiter rateLimiter = RateLimiter.create(2);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
// 获得令牌
rateLimiter.acquire();
System.out.println(LocalDateTime.now());
}).start();
}
输出为如下,可以看到每秒执行2个请求
2020-04-11T20:54:45.254
2020-04-11T20:54:45.554
2020-04-11T20:54:46.054
2020-04-11T20:54:46.555
2020-04-11T20:54:47.068
2020-04-11T20:54:47.554
rateLimiter提供了acquire()和tryAcquire()方法
acquire()方法,如果没有可用令牌,会一直阻塞到获得令牌
tryAcquire()方法,如果没有可用令牌,则直接返回false,可以设置超时获取