限流算法-令牌桶、漏桶算法之java实现

介绍

令牌桶算法:
一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌;如下:
a.假设是2r/s,则每500毫秒添加n个令牌;
b.桶中最多存放x个令牌,桶满时,再添加会拒绝;
c.请求去获取令牌,如果令牌足够,允许通过;如果令牌不足,拒绝请求或放入缓冲区等待;

漏桶算法:
漏桶容量固定,按照固定速率流出水滴;如果桶是空的,不用流,如果桶满了,再流入水滴,则拒绝服务。

区别

令牌桶控制的是平均流入速率,速率可高可低,允许有突发请求;漏桶控制的是恒定的流出速率,从而平滑流入速率。

java实现

令牌桶算法:

package com.cvnavi.oa.report;

import org.apache.kafka.common.protocol.types.Field;

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

public class TokenLimiter {

    private ArrayBlockingQueue<String> blockingQueue;

    //容量大小
    private int limit;

    //令牌的产生间隔
    private int period;

    //令牌每次产生的个数
    private int amount;

    public TokenLimiter(int limit, int period, int amount) {
        this.limit = limit;
        this.period = period;
        this.amount = amount;

        blockingQueue = new ArrayBlockingQueue<>(limit);
    }

    private void init() {
        for(int i = 0; i < limit; i++) {
            blockingQueue.add("lp");
        }
    }

    private void addToken(int amount) {
        for(int i = 0; i < amount; i++) {
            //溢出返回false
            blockingQueue.offer("lp");
        }
    }

    /**
     * 获取令牌
     * @return
     */
    public boolean tryAcquire() {
        //队首元素出队
        return blockingQueue.poll() != null ? true : false;
    }

    /**
     * 生产令牌
     */
    private void start(Object lock) {
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
            synchronized (lock) {
                addToken(this.amount);
                lock.notify();
            }
        }, 500, this.period, TimeUnit.MILLISECONDS);
    }

    /**
     * 先生产2个令牌,减少4个令牌;再每500ms生产2个令牌,减少4个令牌
     */
    public static void main(String[] args) throws InterruptedException {
        int period = 500;
        TokenLimiter limiter = new TokenLimiter(8, period, 2);
        limiter.init();

        Object lock = new Object();
        limiter.start(lock);

        //让线程先产生2个令牌(溢出)
        synchronized (lock) {
            lock.wait();
        }
        for(int i = 0; i < 8; i++) {
            for(int j = 0; j < 4; j++) {
                String s = i + "," + j + ":";
                if(limiter.tryAcquire()) {
                    System.out.println(s + "拿到令牌");
                }
                else{
                    System.out.println(s + "拒绝");
                }
            }
            Thread.sleep(period);
        }
    }

}

漏桶算法:
package com.cvnavi.report;

public class Funnel {

    //漏斗容量
    int capacity;
    //漏水速度
    double leakingRate;
    //漏斗剩余空间(不是水的大小)
    int leftQuota;
    //上次漏斗漏水的时间
    long leakingTs;

    public Funnel(int capacity, double leakingRate, int leftQuota, long leakingTs) {
        this.capacity = capacity;
        this.leakingRate = leakingRate;
        this.leftQuota = leftQuota;
        this.leakingTs = leakingTs;
    }

    private void makeSpace() {
        long nowTs = System.currentTimeMillis();
        //这个是间隔的时间
        long deltaTs = nowTs - this.leakingTs;
        //漏掉的水
        int deltaQuota = (int)(deltaTs * leakingRate);
        //间隔时间太长,溢出
        if (deltaQuota < 0){
            this.leftQuota = capacity;
            this.leakingTs = nowTs;
            return;
        }
        //说明漏的时间不够
        if (deltaQuota < 1) {
            return;
        }
        this.leakingTs = nowTs;
        this.leftQuota = this.leftQuota + deltaQuota;
        if (this.leftQuota > this.capacity) {
            this.leftQuota = this.capacity;
        }
    }

    /**
     * 流入
     * @param quota
     * @return
     */
    public Boolean water(int quota) {
        makeSpace();
        //表示量充足
        if (leftQuota >= quota){
            leftQuota =  leftQuota - quota;
            return true;
        }
        //剩余量不够
        return false;
    }

}


package com.cvnavi.report;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class FunnelLimitRate {

    private static Map<String,Funnel> funnels = new HashMap<>();


    public static void main(String[] args) throws InterruptedException {
        String userId= "2019";
        String actionKey = "rgister";
        int capacity = 8;
        double leakingRate = 0.001;
        int leftQuota = 5;
        Funnel funnel = funnels.get(userId);
        if (funnel == null) {
            funnel = new Funnel(capacity, leakingRate, leftQuota, System.currentTimeMillis());
        }
        //每1000ms(1s),流入1的水
        //每1000ms,流出1000*leakingRate(而且要>1)的水
        for (int i = 0; i < 8; i++) {
            Boolean isBoolean = funnel.water(1);
            TimeUnit.MILLISECONDS.sleep(1000);
            System.out.println(isBoolean + " " + funnel.leftQuota);
        }

    }

}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
令牌算法是一种常见的限流算法,它可以控制请求的速率,防止系统被过多的请求压垮。下面是Java实现令牌算法的步骤和代码逻辑: 1. 定义一个令牌桶类,包含以下属性: - 最后一次令牌发放时间 - 桶的容量 - 令牌生成速度 - 当前令牌数量 2. 实现一个获取令牌的方法,该方法会在每次请求到来时被调用,具体实现如下: - 计算当前令牌数量 - 判断当前令牌数量是否足够 - 如果令牌数量不足,则拒绝请求 - 如果令牌数量足够,则领取令牌,并执行业务逻辑 3. 使用ScheduledExecutorService定时生成令牌,具体实现如下: - 每隔一段时间生成一定数量的令牌 - 如果令牌数量超过桶的容量,则不再生成令牌 下面是Java实现令牌算法的代码逻辑: ``` @Slf4j public class TokensLimiter { private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); // 最后一次令牌发放时间 public long timeStamp = System.currentTimeMillis(); // 桶的容量 public int capacity = 10; // 令牌生成速度10/s public int rate = 10; // 当前令牌数量 public int tokens; public void acquire() { scheduledExecutorService.scheduleWithFixedDelay(() -> { long now = System.currentTimeMillis(); // 当前令牌数 tokens = Math.min(capacity, (int) (tokens + (now - timeStamp) * rate / 1000)); // 每隔0.5秒发送随机数量的请求 int permits = (int) (Math.random() * 9) + 1; log.info("请求令牌数:" + permits + ",当前令牌数:" + tokens); timeStamp = now; if (tokens < permits) { // 若不到令牌,拒绝 log.info("限流了"); } else { // 还有令牌,领取令牌 tokens -= permits; log.info("剩余令牌=" + tokens); } }, 1000, 500, TimeUnit.MILLISECONDS); } public static void main(String[] args) { TokensLimiter tokensLimiter = new TokensLimiter(); tokensLimiter.acquire(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值