漏桶算法和令牌桶算法

目录

1、漏桶算法

2、令牌桶算法

3、两种算法的区别

4、限流工具类RateLimiter

4.1 RateLimiter demo

4.2 主要接口


常用的限流算法有两种:漏桶算法和令牌桶算法。

1、漏桶算法

漏桶算法思路很简单,请求先进入到漏桶里,漏桶以固定的速度出水,也就是处理请求,当水加的过快,则会直接溢出,也就是拒绝请求,可以看出漏桶算法能强行限制数据的传输速率

漏桶算法和令牌桶算法_第1张图片 漏桶算法示意图

但是对于很多场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。

2、令牌桶算法

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务

漏桶算法和令牌桶算法_第2张图片 令牌桶算法示意图

为了更好的理解令牌桶算法,自己造了一个限流算法的简略代码,方便理解。

创建一个需要执行的任务,使用线程池执行。

public class TaskRunnable implements Runnable {
    private Integer reqCount;//已使用令牌数量
    public TaskRunnable(int reqCount) {
        this.reqCount = reqCount;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "已经执行...,目前已使用令牌数:" + reqCount);
    }
}

具体执行逻辑和限流逻辑,//自己造轮子

public class TokenTask {
    //关键:时间控制周期+令牌桶容量
    static volatile long timeStamp = getNowTime();//令牌桶计时开始
    static AtomicInteger reqCount = new AtomicInteger(0);//统计调用数
    static final int maxReqCount = 5;//时间周期内最大请求数
    static final long effectiveDuration = 1000;//时间控制周期(秒)
    /**
     * 限流逻辑
     * @return
     */
    public static boolean passControl() {
        long now = getNowTime();//程序执行时间
        if (now < (timeStamp + effectiveDuration)) {//在时间控制范围内
            reqCount.getAndIncrement();
            return reqCount.get() <= maxReqCount;//当前时间范围内超过最大请求控制数
        } else {
            timeStamp = now;//超时后重置
            reqCount = new AtomicInteger(1);//占用一个令牌
            return true;
        }
    }
    //获取执行时间
    public static long getNowTime() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            //Thread.sleep(200); //用来模拟令牌周期
            if (passControl()) {//放行
                executor.execute(new TaskRunnable(reqCount.get()));
            } else {
                System.out.println("被限流,请稍后访问!");
            }
        }
    }
}

限流效果

漏桶算法和令牌桶算法_第3张图片

 注:本算法只是简单模拟了一下限流流程,不能用于生产。

3、两种算法的区别

漏桶算法输入的时候请求不固定,但都会在漏桶里边先保存起来(小于漏桶的容量),然后输出的时候采用的是恒定的速率执行请求,有点像队列的先进先出,只是队列中的元素出队的时间间隔一致。

//有点类似于使用线程池 new SingleThreadExecutor()-单线程串行,newFixedThreadPool() 固定线程

令牌桶算法跟漏桶算法刚好相反,令牌桶的大小就是接口所能承载的最大访问量,令牌的发放是恒速的,而最终能在某一时间处理的请求数不是恒定的,这取决于单位时间内令牌桶中的令牌数量。

4、限流工具类RateLimiter

Google开源工具包Guava提供了限流工具类RateLimiter,该类基于“令牌桶算法”,非常方便使用。RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率。它支持两种获取Permits接口,一种是如果拿不到立刻返回false,一种会阻塞等待一段时间看能不能拿到。

4.1 RateLimiter demo

//多任务执行,但每秒执行不超过2个任务
final RateLimiter rateLimiter = RateLimiter.create(2.0);
void submitTasks(List tasks, Executor executor) {
    for (Runnable task : tasks) {
        rateLimiter.acquire(); //默认获取1个令牌
        executor.execute(task);
    }
}
//以每秒5kb内的速度发送-限制数据包大小
final RateLimiter rateLimiter = RateLimiter.create(5000.0);
void submitPacket(byte[] packet) {
    rateLimiter.acquire(packet.length);//获取指定数量的令牌
    networkService.send(packet);
}
//以非阻塞的形式达到降级
if(limiter.tryAcquire()) { //未请求到limiter则立即返回false
    doSomething();
}else{
    doSomethingElse();
}

4.2 主要接口

RateLimiter其实是一个abstract类,但是它提供了几个static方法用于创建RateLimiter:

/**
* 创建一个稳定输出令牌的RateLimiter,保证了平均每秒不超过permitsPerSecond个请求
* 当请求到来的速度超过了permitsPerSecond,保证每秒只处理permitsPerSecond个请求
* 当这个RateLimiter使用不足(即请求到来速度小于permitsPerSecond),会囤积最多permitsPerSecond个请求
*/
public static RateLimiter create(double permitsPerSecond);

/**
* 创建一个稳定输出令牌的RateLimiter,保证了平均每秒不超过permitsPerSecond个请求
* 还包含一个热身期(warmup period),热身期内,RateLimiter会平滑的将其释放令牌的速率加大,直到起达到最大速率
* 同样,如果RateLimiter在热身期没有足够的请求(unused),则起速率会逐渐降低到冷却状态
*
* 设计这个的意图是为了满足那种资源提供方需要热身时间,而不是每次访问都能提供稳定速率的服务的情况(比如带缓存服务,需要定期刷新缓存的)
* 参数warmupPeriod和unit决定了其从冷却状态到达最大速率的时间
*/
public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit);

提供了两个获取令牌的方法,不带参数表示获取一个令牌。如果没有令牌则一直等待,返回等待的时间(单位为秒),没有被限流则直接返回0.0

public double acquire();//默认获取一个令牌
public double acquire(int permits);//获取指定令牌数

尝试获取令牌,分为待超时时间和不带超时时间两种:

//尝试获取一个令牌,立即返回
public boolean tryAcquire();
public boolean tryAcquire(int permits);
//尝试获取permits个令牌,带超时时间
public boolean tryAcquire(long timeout, TimeUnit unit);
public boolean tryAcquire(int permits, long timeout, TimeUnit unit);

下篇这篇文章一点要看:

https://zhuanlan.zhihu.com/p/60979444

参考阅读:

流量控制算法——漏桶算法和令牌桶算法 - 知乎

你可能感兴趣的:(算法啊算法,Java,web,开发,令牌桶算法,漏桶算法,RateLimiter)