限流原理与实战

接口限流的算法次要有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...