1. 概述
在高并发访问的情况下,通常会通过限流的方式在控制流量访问问题,以保证服务处在正常压力之下,常见的限流策略有计数器限流、漏桶限流和令牌桶限流
2. 计数器限流
在一段时间间隔内(时间窗口),处理请求的最大数量固定,超过部分不做处理
代码实现如下:
public class CounterServiceImpl implements CounterService {
/**起始时间*/
private long startTime = Instant.now().toEpochMilli();
/**时间区间的时间将毫秒数*/
private long interval = 1000;
/**每秒限制数*/
private long maxCount = 2;
/**累加器*/
private AtomicLong atomicLong = new AtomicLong();
@Override
public long tryAcquire(long taskId, int turn) {
long nowTime = Instant.now().toEpochMilli();
if (nowTime < startTime + interval) {
long count = atomicLong.incrementAndGet();
if (count <= maxCount) {
return count;
} else {
return -count;
}
} else {
synchronized (CounterServiceImpl.class) {
if (nowTime > startTime + interval) {
atomicLong.set(0);
startTime = nowTime;
}
}
return 0;
}
}
}
3. 漏桶限流
漏桶大小固定,处理速度固定,但请求进入的速度不固定,请求过多时,会丢弃多于的请求
限流规则如下:
- 客户端请求以任意速率进入漏桶
- 漏桶的容量是固定的,放行请求速率也是固定的
- 如果处理请求速度太慢,桶内请求会超出桶容量,后面进入的请求就会溢出,即拒绝请求进入
代码实现如下:
@Service
public class LeakBucketServiceImpl implements LeakBucketService {
/**起始时间*/
private long lastOutTime = Instant.now().toEpochMilli();
/**流出速率每秒2次数*/
private long rate = 2;
/**剩余量*/
private long water = 0;
@Override
public synchronized boolean tryAcquire(long taskId, int turn) {
long nowTime = Instant.now().toEpochMilli();
long pastTime = nowTime - lastOutTime;
//漏出量 = 过去时间 * 速率
long outWater = pastTime * rate / 1000;
//剩余水量 = 上次遗留水量 - 漏出水量
water = water - outWater;
//纠正剩余水量
if (water < 0) {
water = 0;
}
//剩余水量小于等于1,则放行
if (water <= 1) {
lastOutTime = nowTime;
water++;
return false;
} else {
return true;
}
}
}
4. 令牌桶限流
令牌桶大小固定,令牌的产生速度固定,但是消耗令牌(请求)的速度不固定,每个请求都会从令牌桶中获取令牌,如果没有令牌,就丢弃这次请求
限流规则如下:
- 程序安装某个速度向桶中放入令牌
- 令牌的容量是固定的,放行的速度是不固定的,只要桶中还有剩余令牌,一旦请求过来就能申请成功,然后放行
- 如果令牌的发放速度慢于请求到来的速度,桶内就没有令牌可以领取,新来的请求就会被拒绝
代码实现如下:
@Service
public class TokenBucketServiceImpl implements TokenBucketService {
/**上一次令牌发放时间*/
private long lastTime = Instant.now().toEpochMilli();
/**桶容量*/
private int capacity = 2;
/**令牌生成速度 个/秒*/
private int rate = 2;
/**当前令牌数*/
private int tokens;
@Override
public synchronized boolean tryAcquire(long taskId, int applyCount) {
long nowTime = Instant.now().toEpochMilli();
//时间间隔,毫秒
long gap = nowTime - lastTime;
tokens = Math.min(capacity, (int) (tokens + gap * rate / 1000));
if (tokens < applyCount) {
return true;
} else {
tokens -= applyCount;
lastTime = nowTime;
return false;
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/76739.html