·  阅读 221

# 限流原理与实战

## 计数器限流原理

``````@Slf4j
public class CountLimiter {

private static long startTime = System.currentTimeMillis();
//时间间隔
private static long interval = 1000;
//时间间隔内最大处理请求数
private static long maxCount = 2;
//计数器
private static AtomicInteger accumulator = new AtomicInteger();

//在1秒内，只允许2个请求接入，如若查过时间片，则初始化参数进入新的一轮时间片
private static long tryAcquire(long taskId, int turn){
long nowTime = System.currentTimeMillis();
//在时间段内，且数量小于等于最大允许请求值，则返回数量
if (nowTime < startTime + interval){
int count = accumulator.incrementAndGet();
if (count <= maxCount){
return count;
}else {
return -count;
}
}else {
//不为一个时间段，则重置计数器和开始时间
synchronized (CountLimiter.class){
if (nowTime > startTime + interval){
accumulator.set(0);
startTime = nowTime;
}
}
return 0;
}
}

@Test
public void testLimit(){
AtomicInteger limited = new AtomicInteger(0);
final int turns = 20;
long start = System.currentTimeMillis();
for (int i = 0; i < threads;i++){
pool.submit(() -> {
try {
for (int j = 0; j < turns; j++) {
if (index <= 0){
limited.getAndIncrement();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

float time = (System.currentTimeMillis() - start) /1000F;
log.info("限制次数为：" + limited.get() + "，通过次数为:"+(threads*turns - limited.get()));

log.info("运行时长："+time);
}
}

# 漏桶限流原理和Java参考实现

## 漏桶限流的基本原理

1）水通过进水口（对应客户端请求）以任意速率流入漏桶。 2）漏桶的容量是固定的，出水（放行）速率也是固定的。 3）漏桶容量是不变的，如果处理速度太慢，桶内水量会超出桶的容量，后面流入的水就会溢出，表示请求拒绝。

``````    private static long lastOutTime = System.currentTimeMillis();

//流出速率每秒2个
private static int rate = 2;

//剩余水的量
private static long water = 0;

/**
* false:没有被限制
* true:被限流
* @param turns
* @return
*/
public synchronized static boolean tryAcquire(long taskId, int turns){
long now = System.currentTimeMillis();
long pastTime = now - lastOutTime;
long outWater = pastTime * rate/ 1000;
water = water -outWater;
log.info("water {} pastTime {} outWater {}",water ,pastTime, outWater);

if (water < 0){
water = 0;
}
if (water <= 1){
lastOutTime = now;
water ++ ;
return false;
}else {
return true;
}
}

# 令牌桶限流原理和Java参考实现

## 令牌桶限流大致的规则如下：

（1）进水口按照某个速度向桶中放入令牌。 （2）令牌的容量是固定的，但是放行的速度是不固定的，只要桶中还有剩余令牌，一旦请求过来就能申请成功，然后放行。 （3）如果令牌的发放速度慢于请求到来的速度，桶内就无令牌可领，请求就会被拒绝。

``````@Slf4j
public class TokenBucketLimiter {
//上一次令牌发放的时间
public long lastTime = System.currentTimeMillis();
//桶的容量
public int capacity = 2;
//令牌生成速度个/秒
public int rate = 2;
//当前令牌的数量
public int tokens;

//返回值说明
/**
* false:没有被限制
* true:被限流
* @param turns
* @return
*/
public synchronized boolean tryAcquire(long taskId, int applyCount){
long now = System.currentTimeMillis();
//时间间隔
long gap = now - lastTime;
//当前令牌数
tokens = Math.min(capacity, (int)(tokens+gap*rate/1000));
log.info("tokens {} capacity {} gap {}",tokens ,capacity, gap);
if (tokens < applyCount){
return true;
}else {
tokens -= applyCount;
lastTime = now;
log.info("剩余令牌.." + tokens);
return false;
}
}

@Test
public void testLimit(){
AtomicInteger limited = new AtomicInteger(0);
final int turns = 20;
long start = System.currentTimeMillis();
for (int i = 0; i < threads;i++){
pool.submit(() -> {
try {
for (int j = 0; j < turns; j++) {
if (isLimited){
limited.getAndIncrement();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

float time = (System.currentTimeMillis() - start) /1000F;
log.info("限制次数为：" + limited.get() + "，通过次数为:"+(threads*turns - limited.get()));

log.info("运行时长："+time);

}

}