限流原理与实战
接口限流的算法次要有3种,别离是计数器限流、漏桶算法和令牌桶算法
计数器限流原理
原理:在一个工夫窗口(距离)内,所解决的申请的最大数量是有限度的,对超过限度的局部申请不做解决。
代码实现
@Slf4jpublic 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){ log.info("新工夫区到了,taskId{}, turn{}..", taskId, turn); if (nowTime > startTime + interval){ accumulator.set(0); startTime = nowTime; } } return 0; } } private ExecutorService pool = Executors.newFixedThreadPool(10); @Test public void testLimit(){ AtomicInteger limited = new AtomicInteger(0); final int threads = 2; final int turns = 20; CountDownLatch countDownLatch = new CountDownLatch(threads); long start = System.currentTimeMillis(); for (int i = 0; i < threads;i++){ pool.submit(() -> { try { for (int j = 0; j < turns; j++) { long taskId = Thread.currentThread().getId(); long index = tryAcquire(taskId, j); if (index <= 0){ limited.getAndIncrement(); } Thread.sleep(200); } } 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("限度比例为:"+(float)limited.get()/((float) threads*turns)); 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 taskId * @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)如果令牌的发放速度慢于申请到来的速度,桶内就无令牌可领,申请就会被回绝。
@Slf4jpublic class TokenBucketLimiter { //上一次令牌发放的工夫 public long lastTime = System.currentTimeMillis(); //桶的容量 public int capacity = 2; //令牌生成速度个/秒 public int rate = 2; //以后令牌的数量 public int tokens; //返回值阐明 /** * false:没有被限度 * true:被限流 * @param taskId * @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){ log.info("被限流了.. {} ,applyCount:{}",taskId,applyCount); return true; }else { tokens -= applyCount; lastTime = now; log.info("残余令牌.." + tokens); return false; } } private ExecutorService pool = Executors.newFixedThreadPool(10); @Test public void testLimit(){ AtomicInteger limited = new AtomicInteger(0); final int threads = 2; final int turns = 20; CountDownLatch countDownLatch = new CountDownLatch(threads); long start = System.currentTimeMillis(); for (int i = 0; i < threads;i++){ pool.submit(() -> { try { for (int j = 0; j < turns; j++) { long taskId = Thread.currentThread().getId(); boolean isLimited = tryAcquire(taskId, 1); if (isLimited){ limited.getAndIncrement(); } Thread.sleep(200); } } 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("限度比例为:"+(float)limited.get()/((float) threads*turns)); log.info("运行时长:"+time); }}
参考:《2020最新Java根底精讲视频教程和学习路线!》
链接:https://juejin.cn/post/693747...