public class Counter { public long timeStamp = getNowTime(); public int reqCount = 0; public final int limit = 100; // 时间窗口内最大请求数 public final long interval = 1000; // 时间窗口ms public boolean grant() { long now = getNowTime(); if (now < timeStamp + interval) { // 在时间窗口内 reqCount++; // 判断当前时间窗口内是否超过最大请求控制数 return reqCount <= limit; } else { timeStamp = now; // 超时后重置 reqCount = 1; return true; } } }
//一个并发安全map,skip list有序 this.measurements = new ConcurrentSkipListMap<Long, Long>();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/** * 获取map的key,以时间纳秒值为key * @return */ private long getTick() { for (; ; ) { final long oldTick = lastTick.get(); //每纳秒处理的请求很多,减少compareAndSet的失败次数,这儿*COLLISION_BUFFER final long tick = clock.getTick() * COLLISION_BUFFER; // ensure the tick is strictly incrementing even if there are duplicate ticks final long newTick = tick > oldTick ? tick : oldTick + 1; if (lastTick.compareAndSet(oldTick, newTick)) { return newTick; } } }
private int window; //计算窗口 //整个循环数组窗口 private int ringWindow=window+30;
requestCounter = new AtomicInteger[ringWindow]; failedCounter = new AtomicInteger[ringWindow]; for (int i = 0; i < ringWindow; i++) { requestCounter[i] = new AtomicInteger(0); failedCounter[i] = new AtomicInteger(0); }
private long countTotal(AtomicInteger[] caculateCounter){ int currentIndex = getIndex(); long sum = 0; for (int i = 0; i < window; i++) { //这儿并不是直接计算window中的所有counter, //而是从currentIndex向前倒取window个 int index = ((currentIndex + ringWindow) -i) % this.ringWindow; sum += caculateCounter[index].get(); } return sum; }
为什么需要ringWindow,直接window就可以?这儿有个奇技淫巧。
1 2 3 4 5 6 7 8 9 10 11
CLEAN_UP_BUFFER=10;
public void cleanupFutureCounter() { int currentIndex = getIndex(); for (int i = 1 ; i <= CLEAN_UP_BUFFER; i++) { int index = (currentIndex + i) % this.ringWindow; requestCounter[index].set(0); failedCounter[index].set(0); } }
public class LeakyDemo { public long timeStamp = getNowTime(); public int capacity; // 桶的容量 public int rate; // 水漏出的速度 public int water; // 当前水量(当前累积请求数) public boolean grant() { long now = getNowTime(); water = max(0, water - (now - timeStamp) * rate); // 先执行漏水,计算剩余水量 timeStamp = now; if ((water + 1) < capacity) { // 尝试加水,并且水还未满 water += 1; return true; } else { // 水满,拒绝加水 return false; } } }
public class TokenBucketDemo { public long timeStamp = getNowTime(); public int capacity; // 桶的容量 public int rate; // 令牌放入速度 public int tokens; // 当前令牌数量 public boolean grant() { long now = getNowTime(); // 先添加令牌 tokens = min(capacity, tokens + (now - timeStamp) * rate); timeStamp = now; if (tokens < 1) { // 若不到1个令牌,则拒绝 return false; } else { // 还有令牌,领取令牌 tokens -= 1; return true; } } }