一、对单个接口的总并发数进行限制
1、利用一个AtomicLong变量进行简单粗暴的限流
try {
if(atomic.incrementAndGet() > 限流数) {
//拒绝请求
}
//处理请求
} finally {
atomic.decrementAndGet();
}
2、利用SHARED
模式的ReentrantLock
try {
if(lock.lock()) {
//拒绝请求
}
//处理请求
} finally {
lock.unlock();
}
这里可以根据需要采用完全阻塞的lock,立即返回的tryLock
,以及带等待时间的lock(lockTime)
等方法。
3、异步
对于异步需要注意,如果consumer方
没有回调或者回调超时,要保证对应请求的decrement
和unlock
操作都要执行,否则计数就会有问题了。
二、限流某个接口的时间窗请求数
LoadingCache<Long, AtomicLong> counter =
CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.SECONDS)
.build(new CacheLoader<Long, AtomicLong>() {
@Override
public AtomicLong load(Long seconds) throws Exception {
return new AtomicLong(0);
}
});
long limit = 1000;
while(true) {
//得到当前秒
long currentSeconds = System.currentTimeMillis() / 1000;
if(counter.get(currentSeconds).incrementAndGet() > limit) {
System.out.println("限流了:" + currentSeconds);
continue;
}
//业务处理
}
这里统计每秒内的请求次数总和,过期时间设置为大于1s的时间即可,保证1s内的统计不会过期。
和上面段落实现功能的区别:
上面限制总并发数的限制,如果业务流执行很快,很快就decrementAndGet或者unlock,那么1s内可以接收很多请求,比如业务处理消耗1ms,即使并发数最大是1,1s中也可以接收1000个请求。
本段落限制时间窗内的请求数就和业务执行快慢无关了,这里只统计1s钟内执行了多少次,即使业务处理消耗时间很少,比如1ms,但是如果在第一个ms内就来了1000个请求,那么剩余的999ms就一个请求也进不来了。
三、分布式限流
用redis实现的分布式限流。(换成redis的方式)
int current = jedis.incr(key);
if (current + 1 > limit) //如果超出限流大小
return 0;
else if (current == 1) //只有第一次访问需要设置2秒的过期时间
jedis.expire(key, "2");
return 1