背景
开发过程中经常会遇到一些限流的场景,比如为保证服务器自身安全,对接口进行限流;在我们请求一个三方接口时,对方有1分钟内调用次数不能超过多少次的限制。等等。这里先搜罗一些实现方案,具体原理后面慢慢学习补充
常用的限流方式和场景有:
- 限制总并发数(比如数据库连接池、线程池)
- 限制瞬时并发数(如nginx的limitconn模块,用来限制瞬时并发连接数,Java的Semaphore也可以实现)
- 限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limitreq模块,限制每秒的平均速率)
- 其他:比如如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。
我们常说的限流,其实更多的都是指时间窗口内的平均速率。
实现方案
Guava RateLimiter
RateLimiter是基于令牌桶算法实现的一个限流组件。是一个单机版的限流组件
使用方法:
@Test
public void test2(){
// 每一秒可以直行多少次。每一秒放出一个令牌
RateLimiter rateLimiter = RateLimiter.create(1d);
for (int i = 0; i < 10; i++) {
rateLimiter.acquire();
System.out.println(i);
}
}
Redission RRateLimiter
RRateLimiter,是redission实现的一个分布式的限流
使用方法:
@Bean("rRateLimiter")
public RRateLimiter rateLimiter(RedissonClient redissonClient) {
long limit = 1;
long rate = 1;
RRateLimiter rRateLimiter = redissonClient.getRateLimiter("test-rRateLimiter");
// OVERALL: 这表示整个 RateLimiter 实例的速率限制,即所有 RateLimiter 实例的总速率。
// PER_CLIENT: 这表示每个客户端(即每个 RateLimiter 实例)的速率限制。在这种类型下,每个 RateLimiter 实例都有独立的速率限制。
boolean b = rRateLimiter.trySetRate(RateType.PER_CLIENT, rate, limit, RateIntervalUnit.SECONDS);
log.info("添加测试限流器:"+b);
log.info("test-rRateLimiter " + JSON.toJSONString(rRateLimiter.getConfig()));
return rRateLimiter;
}
@Test
public void test2(){
for (int i = 0; i < 10; i++) {
rRateLimiter.acquire();
System.out.println(i);
}
}