令牌桶限流-java实现

本文详细介绍了如何使用Java实现令牌桶算法,用于流量控制,包括桶容量、令牌速率和请求处理逻辑。通过伪代码和实际代码展示了如何维护请求时间与剩余令牌状态,并确保不超过桶容量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

请添加图片描述
令牌桶的容量是c(个),令牌以速度r(个/秒)均匀的放入桐中,上个请求的时间为at(时间戳),上个请求后剩余的令牌数目为w(个),现在有个请求b对象进来了,现在请求的时间bt=now(),伪代码如上图,其中wb代表从at到bt时间段内产生的令牌数,产生的令牌数加上上次剩余的令牌数是不能大于桶容量的
Java实现代码:

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

public class BucketLimiter {
    /* 桶容量 */
    private final long c;
    /* 令牌放入速度 */
    private final int r;
    /* 桶初始化时间 */
    private final long initTime;

    /* 模拟redis缓存 */
    private final Map<String, Object> redisCache = new HashMap<>(2048);
    /* 请求时间redis key */
    public static final String REQUEST_TIME = "REQUEST_TIME";
    /* 请求后剩余令牌数redis key */
    public static final String TOKEN_REMAINS = "TOKEN_REMAINS";

    /**
     * @param c 桶容量
     * @param r 令牌放入速度
     */
    public BucketLimiter(long c, int r) {
        this.c = c;
        this.r = r;
        this.initTime = System.currentTimeMillis();
    }

    /* 注意这里要加同步 */
    public synchronized boolean permit() {
        //获取上个请求后令牌桶状态
        BucketStatus status = getLastRequestBucketStatus();

        //上个请求时间
        final long at = status.getRequestTime();
        //上个请求后剩余令牌数
        long w = status.getRemains();
        //现请求时间
        final long bt = System.currentTimeMillis();
        //从上个请求到现在请求增加的令牌数
        final long wb = (bt - at)/1000 * r;
        System.out.println("===生产数:"+wb);
        //现在桶里面剩余的令牌数
        w = Math.min(w + wb, c);
        //假设每次消耗一个令牌
        if (w > 0) {
            w--;
            //请求时间和剩余令牌数
            redisCache.put(REQUEST_TIME, bt);
            redisCache.put(TOKEN_REMAINS, w);
            return true;
        } else {
            return false;
        }
    }

    /**
     * @description 获取上个请求后令牌桶状态
     * @author yulin.chen
     * @date 2021/10/26 10:27
     */
    private BucketStatus getLastRequestBucketStatus() {
        Object requestTime = redisCache.get(REQUEST_TIME);
        Object remains = redisCache.get(TOKEN_REMAINS);
        if (null == requestTime) {
            //请求时间为空就是第一次请求,剩余数设置为0,时间设置为初始化时间
            return new BucketStatus(0L, this.initTime);
        }
        return new BucketStatus(Long.parseLong(String.valueOf(remains)), Long.parseLong(String.valueOf(requestTime)));
    }

    /**
     *
     */
    public static class BucketStatus {
        /* 剩余令牌数 */
        private final Long remains;
        /* 请求时间 */
        private final Long requestTime;

        public BucketStatus(Long remains, Long requestTime) {
            this.remains = remains;
            this.requestTime = requestTime;
        }

        public Long getRemains() {
            return remains;
        }

        public Long getRequestTime() {
            return requestTime;
        }
    }
}

测试代码

public static void main(String[] args) throws InterruptedException {
    BucketLimiter limiter = new BucketLimiter(5000, 5);
    //生产5秒,每秒生产5个
    TimeUnit.SECONDS.sleep(5);
    for (int i = 0; i < 10000; i++) {
        new Thread(() -> System.out.println(Thread.currentThread().getName() + " request permit:" + limiter.permit()), "[Thread " + i + "]")
                .start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值