工作中对外提供的API 接口设计很多时候要考虑限流,如果不考虑,可能会造成系统的连锁反应,轻者响应缓慢,重者系统宕机。而常用的限流算法有令牌桶算法和漏桶算法,本篇介绍令牌桶算法
1、令牌桶算法
原理如上图,系统以恒定速率不断产生令牌,令牌桶有最大容量,超过最大容量则丢弃,同时用户请求接口,如果此时令牌桶中有令牌则能访问获取数据,否则直接拒绝用户请求
2、java代码实现
/**
* 线程池每0.5s发送随机数量的请求,每次请求计算当前的令牌数量,请求令牌数量超出当前令牌数量,则产生限流
*/
@Slf4j
public class TokensLimiter {
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
// 最后一次令牌发放时间
public long timeStamp = System.currentTimeMillis();
// 桶的容量
private int capacity = 7;
// 令牌生成速度5/s
private int rate = 5;
// 当前令牌数量
private int tokens;
public void acquire() {
//令牌生成速度 = 5/1s 此次时间-上次生成时间=中间耗费时间
scheduledExecutorService.scheduleWithFixedDelay(() -> {
long now = System.currentTimeMillis();
long tokensCal = tokens + (now - timeStamp) * rate/1000;
int tokenCalInt = (int)tokensCal;
// 当前令牌数
tokens = Math.min(capacity,tokenCalInt);
//每隔0.5秒发送随机数量的请求
int permits = (int) (Math.random() * 9) + 1;
log.info("请求令牌数:" + permits + ",当前令牌数:" + tokens);
timeStamp = now;
if (tokens < permits) {
// 若不到令牌,则拒绝
log.info("限流了");
} else {
// 还有令牌,领取令牌
tokens -= permits;
log.info("剩余令牌=" + tokens);
}
}, 1000, 500, TimeUnit.MILLISECONDS);
//1秒以后开始执行第一次任务,第一次执行完每隔500ms执行下次任务
}
public static void main(String[] args) {
TokensLimiter tokensLimiter = new TokensLimiter();
tokensLimiter.acquire();
}
}
输出结果: