美文网首页
令牌桶算法、漏桶算法

令牌桶算法、漏桶算法

作者: 放开那个BUG | 来源:发表于2020-12-26 21:46 被阅读0次

1、前言

网上很多人说漏桶、令牌桶算法,其实乍一看好像都差不多,但是漏桶因为通过的速率恒定,应对不了突发流量,所以一般都用令牌桶。所谓的速率恒定可以看下面这个例子:
假设漏桶容量为10,每次下漏速度为1,令牌桶的容量也是10。如果同一时间来了100个请求,那么针对漏桶来说,它只能接受10个请求,剩余90个丢弃,令牌桶也只能接受10个请求,拒绝90个。但是后续流程中,漏桶的漏下的速率只能是1,但是令牌桶在同一时间能发出10个令牌,通过10个请求,所以令牌桶能接受短暂的尖刺流量。

2、漏桶算法和令牌桶算法

漏桶的示意图如下:

漏桶算法
漏桶算法:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author xushu
 * @create 12/26/20 8:46 PM
 * @desc
 */
public class LeakyBucketLimiter {

    private Logger logger = LoggerFactory.getLogger(LeakyBucketLimiter.class);

    private ScheduledExecutorService service = Executors.newScheduledThreadPool(5);

    // 桶容量
    private int capicaty = 10;

    // 当前流出水量
    private int water = 0;

    // 桶流水速率  rate/s
    private int rate = 4;

    // 上次放水入桶时间
    private long lastTime = System.currentTimeMillis();


    public void accquire(){
        service.scheduleAtFixedRate(() -> {
            // 记录当前时间
            long now = System.currentTimeMillis();

            // 计算流出水量
            water = Math.max(0, water - (int)(now - lastTime) / 1000 * rate);

            // 模拟当前请求 1 ~ 8
            int request = (int)(Math.random() * 8) + 1;

            logger.info("当前请求:" + request + ",桶剩余容量:" + (capicaty - water));

            lastTime = now;

            if(capicaty - water < request){
                int permits = capicaty - water;
                water += permits;
                logger.info("限流了,只允许" + permits + "个请求,其余" + (request - permits) + "个丢弃,桶剩余容量为" + (capicaty - water));
            }else {
                water += request;
                logger.info("允许了" + request + "个请求,桶剩余容量为" + (capicaty - water));
            }

        }, 3, 1, TimeUnit.SECONDS);
    }


    public static void main(String[] args) {
        LeakyBucketLimiter leakyBucketLimiter = new LeakyBucketLimiter();
        leakyBucketLimiter.accquire();
    }
}

令牌桶示意图如下:

令牌桶
令牌桶算法:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author xushu
 * @create 12/26/20 9:28 PM
 * @desc
 */
public class TokensLimiter {

    private Logger logger = LoggerFactory.getLogger(TokensLimiter.class);

    private ScheduledExecutorService service = Executors.newScheduledThreadPool(5);

    // 令牌桶的容量
    private int capicaty = 10;

    // 令牌生成速率  /s
    private int rate = 4;

    // 上次令牌生成时间
    private long lastTime = System.currentTimeMillis();

    // 当前生成令牌数
    private int currentTokens;

    public void accquire(){
        service.scheduleAtFixedRate(() -> {
            long now = System.currentTimeMillis();

            // 当前令牌生成数量
            currentTokens = Math.min(capicaty, currentTokens + (int)(now - lastTime) / 1000 * rate);

            // 当前请求数量 1 ~ 8
            int request = (int)(Math.random() * 8) + 1;

            logger.info("请求令牌数:" + request + ",当前令牌数:" + currentTokens);

            lastTime = now;

            if(currentTokens < request){
                int permits = currentTokens;
                currentTokens = 0;
                logger.info("限流了,只允许拿" + permits +  "个令牌,其余" + (request - permits)  + "个请求被拒绝");
            }else {
                currentTokens -= request;
                logger.info("剩余令牌数:" + currentTokens);
            }
        }, 3, 1, TimeUnit.SECONDS);
    }


    public static void main(String[] args) {
        TokensLimiter tokensLimiter = new TokensLimiter();
        tokensLimiter.accquire();
    }

}

一般工程上还是使用 Guava 的 RateLimiter 来使用令牌桶算法,他的使用也比较简单:

import com.google.common.util.concurrent.RateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author xushu
 * @create 12/26/20 9:57 PM
 * @desc
 */
public class RateLimiterTest {

    private static Logger logger = LoggerFactory.getLogger(RateLimiterTest.class);

    public static void main(String[] args) {
        // 每秒3个
        RateLimiter limiter = RateLimiter.create(2);

        // 此 for 循环一般计算机允许的很快,但是因为 RateLimiter 的限制,所以每秒只能打印2个
        for(int i = 0; i < 1000; i++){
            limiter.acquire();
            logger.info("用户" + i + "获取到了");
        }
    }
}

3、总结

漏桶跟令牌桶区别在于,漏桶能够强行限制数据的传输速率,而令牌桶能够在限制数据的传输速率的基础上,还允许突发流量,相对而言适用范围广一些。

相关文章

网友评论

      本文标题:令牌桶算法、漏桶算法

      本文链接:https://www.haomeiwen.com/subject/nymgnktx.html