计数器法
public class Counter {private long timestamp = System.currentTimeMillis(); //当前时间窗口起始时间private int reqCount = 0; //当前时间窗口计数器(请求数)private final int reqLimitCount = 100; //限流阈值private final long intervalMs = 1000; //时间窗口 ms//返回false限流,true不限流public boolean limit() {long now = System.currentTimeMillis();if (now < timestamp + intervalMs) { //在当前时间窗口reqCount++; //递增请求数return reqCount < reqLimitCount; //判断请求数是否小于阈值} else { //不在当前时间窗口timestamp = now; //重置当前时间窗口起始时间reqCount = 1; //重置当前时间窗口内请求数return true;}}
}
滑动时间窗口
* 示例:
* 模拟1s限制100个请求
*
* 滑动时间窗口将1s划分10个格子,每个格子100ms,每100ms滑动时间窗口向右滑动一次(增加一个格子,移除第一个格子),每个格子计数器记录了到此刻的总请求数
* 例如这个滑动时间窗口[10,20,30,40,50,60,70,80,90,100] ,截止到第一个格子的时间请求数10,截止到第10个格子的时间请求数100,1s时间窗口累计请求数为100-10=90个请求
public class SlideTimeWindow {private volatile long reqCount=0; //当前累计请求数,可存储在redis中,实现分布式集群请求次数统计private long reqLimitCount = 100; //限流阈值private volatile boolean pass=true; //是否允许请求通过,true 允许通过,false 不允许通过public static void main(String[] args) throws InterruptedException {SlideTimeWindow slideTimeWindow = new SlideTimeWindow();//启动滑动时间窗口限流new Thread(()->{try {slideTimeWindow.limit();} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();//模拟应用请求不断的到来while (true){ //模拟在未被限流时,随机10ms内,就会来一个请求if(slideTimeWindow.pass){slideTimeWindow.reqCount=slideTimeWindow.reqCount+1;}TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 10));}}public void limit() throws InterruptedException {LinkedList<Long> slots = new LinkedList<>(); //滑动时间窗口,假设滑动时间窗口划分了10个格子,格子存储的是截止到当前窗格累计的请求数while (true) {slots.add(reqCount); if (slots.size() > 10) { //滑动时间窗口超过10个格子,向后滑动一个格子 slots.removeFirst();}if (slots.peekLast() - slots.peekFirst() > reqLimitCount) { //获取滑动时间窗口内累计请求数,并与限流阈值比较,决定是否限流System.out.println("被限流了 "+slots);pass = false; } else {System.out.println("未限流 "+slots);pass = true;}TimeUnit.MILLISECONDS.sleep(100);}}}
漏桶
public class LeakyBucket {private long capacity; //漏桶容量(限流阈值)private long rate; //每秒流出速率(每秒流出的请求数量)private long water; //漏桶中当前水量 (当前的请求数)private long timestamp=System.currentTimeMillis(); //上次加水时间(请求时间) //是否允许请求通过 true 通过,false 拒绝public boolean pass(){long now=System.currentTimeMillis();//执行漏水,更新当前水量water= Math.max(0,water-(now-timestamp)/1000*rate); timestamp=now;if(water+1<capacity){water++;System.out.println("加水成功"); //不需要限流return true;}else{System.out.println("漏桶满了 加水失败"); //需要限流return false;}}}
令牌桶
public class TokenBucket {private long capacity; //令牌桶容量private long rate; //颁发令牌的速率(秒)private long tokens; //令牌桶中当前令牌数量private long timestamp = System.currentTimeMillis(); //上次颁发令牌时间//是否允许请求通过 true 通过,false 拒绝public boolean pass() {long now = System.currentTimeMillis();//颁发令牌,更新桶中当前令牌数量tokens = Math.min(capacity, tokens + (now - timestamp) / 1000 * rate);timestamp = now;if (tokens < 1) { //没有令牌,请求拒绝return false;} else { //还有令牌,消耗一个令牌,请求通行tokens--;return true;}}
}