令牌桶算法

一 算法

令牌桶算法和漏桶算法不同的是,有时后端能够处理一定的突发情况,只是为了系统稳定,一般不会让请求超过正常情况的60%,给容灾留有余地。但漏桶算法中后端处理速度是固定的,对于短时的突发情况,后端不能及时处理,和实际处理能力不匹配。

令牌算法是以固定速度往一个桶内增加令牌,当桶内令牌满了后,就停止增加令牌。上游请求时,先从桶里拿一个令牌,后端只服务有令牌的请求,所以后端处理速度不一定是匀速的。当有突发请求过来时,如果令牌桶是满的,则会瞬间消耗桶中存量的令牌。如果令牌还不够,那么再等待发放令牌(固定速度),这样就导致处理请求的速度超过发放令牌的速度。

如下图,灰色部分是令牌桶,有容量限制,只能最多存放 capacity 个令牌,每秒以固定的速度向桶中增加令牌,如果桶的容量满了,则等待桶中令牌被消耗后,再增加令牌。另一边应用进程拿到令牌后才处理请求,如果没有拿到令牌,则不处理该请求。

1 有一个固定容量的桶存放令牌(Token)。

2 桶初始化是空的,以固定的速度(rate)向桶里填充 Token,当达到桶的容量时,多余的令牌被丢弃。

3 当请求到来时,从桶里移除一个令牌,如果没有令牌,则拒绝该请求。

令牌桶控制的是令牌入桶的速度,对于拿到令牌的速度没有限制,允许一定的突发流量被瞬间处理。

二 需求

1 设计一个令牌桶,能放 2000 个令牌,放令牌的速度是每毫秒1个令牌。即1秒放1000个令牌。

2 外部最大请求为 1500 个,并随机取值,会限流吗?

三 代码

package currentLimit;

import java.util.Random;

/**
* @className: TokenBucket
* @description: 令牌桶算法
* @date: 2022/1/8
* @author: cakin
*/
public class TokenBucket {
    static long cur = 0; // 当前桶中令牌数量
    // 记录上次统计时间的毫秒
    static long lastTime = System.currentTimeMillis();

    /**
     * 功能描述:漏桶算法
     *
     * @param capcity 桶能承载的最大令牌数
     * @param rate    放入令牌的速度,系统每毫秒放入的令牌数
     * @return true:限流 false:不限流
     * @author cakin
     * @date 2022/1/9
     */
    static boolean TokenBucket(int capcity, int rate) {
        // 当前毫秒
        long millisTime = System.currentTimeMillis();
        // 两次请求的间隔,单位是毫秒
        long time = (millisTime - lastTime);
        // 当前桶中的令牌数
        cur = Math.min(capcity, cur + time * rate);
        lastTime = millisTime;

        if (cur == 0) {
            // 没有令牌拿
            return true;
        }
        // 拿走一块令牌
        --cur;
        return false;
    }

    // 测试方法
    public static void main(String[] args) {
        for (; ; ) {
            // 停顿1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 模拟1秒请求的次数 1500次内
            int randomTime = new Random().nextInt(1500);  // 通过调这个值,来测试是否限流
            for (int j = 0; j < randomTime; j++) {
                request();
            }
        }
    }

    /**
     * 功能描述:模拟请求
     *
     * @author cakin
     * @date 2022/1/9
     */
    private synchronized static void request() {
        if (TokenBucket(2000, 1)) {
            System.out.println("限流了" + cur);
        } else {
            System.out.println("没限流" + cur);
        }
    }
}

四 测试

五 说明

从测试来看,当外部请求的的请求数在1500内随机取值,还是可能限流的。

计数器算法、滑动窗口算法、漏桶算法、令牌桶算法,这几种算法的功能逐渐增强,但实现的难度也逐渐增大。由于过载保护是一个通用的功能,一般都在框架底层实现,所以采用令牌桶算法是较好的选择之一。实现一次,可以在很多模块上复用。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>