Redis令牌桶(限流算法)

Redis令牌桶(Token Bucket)是一种常用的限流算法,可以用来控制请求的速率,防止系统因过多请求而崩溃。令牌桶算法基于一个简单的概念:在固定的时间间隔内生成一定数量的令牌,每个请求需要消耗一个令牌,如果令牌不足,请求将被拒绝或等待。下面是Redis令牌桶的详细讲解。

令牌桶算法原理

  1. 令牌桶:令牌桶是一个容器,用于存放令牌。令牌以固定的速率被加入到桶中,桶有一个最大容量,超过这个容量的令牌将被丢弃。
  2. 请求处理:每个请求需要从桶中获取一个令牌才能被处理。如果桶中没有令牌,请求要么被拒绝,要么等待直到有令牌可用。
  3. 生成令牌:令牌按照固定的时间间隔生成,例如每秒生成10个令牌。
  4. 消耗令牌:每个请求消耗一个令牌,当令牌不足时,请求会被限流。

实现Redis令牌桶

通过Redis的INCREXPIRE等原子操作,可以实现分布式环境下的令牌桶算法。下面是一个示例实现:

Lua脚本实现

Lua脚本可以保证操作的原子性,在Redis中执行多个命令时避免竞争条件。

-- redis_token_bucket.lua
local key = KEYS[1] -- 令牌桶的键名
local rate = tonumber(ARGV[1]) -- 令牌生成速率 (每秒生成的令牌数)
local capacity = tonumber(ARGV[2]) -- 令牌桶的容量
local now = tonumber(ARGV[3]) -- 当前时间戳(毫秒)
local requested = tonumber(ARGV[4]) -- 请求消耗的令牌数

-- 获取桶的信息
local last_tokens = tonumber(redis.call('get', key .. ':tokens')) or capacity
local last_refreshed = tonumber(redis.call('get', key .. ':timestamp')) or now

-- 计算生成的令牌数
local delta = math.max(0, now - last_refreshed) * rate / 1000
local filled_tokens = math.min(capacity, last_tokens + delta)

-- 检查是否有足够的令牌
if filled_tokens < requested then
    return {0, filled_tokens} -- 令牌不足,返回失败
else
    -- 更新令牌桶信息
    filled_tokens = filled_tokens - requested
    redis.call('set', key .. ':tokens', filled_tokens)
    redis.call('set', key .. ':timestamp', now)
    return {1, filled_tokens} -- 请求成功,返回剩余令牌数
end
Java代码调用Lua脚本
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class TokenBucket {
    private final JedisPool jedisPool;
    private final String script;

    public TokenBucket(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
        // Lua脚本内容
        this.script = "local key = KEYS[1] " +
                      "local rate = tonumber(ARGV[1]) " +
                      "local capacity = tonumber(ARGV[2]) " +
                      "local now = tonumber(ARGV[3]) " +
                      "local requested = tonumber(ARGV[4]) " +
                      "local last_tokens = tonumber(redis.call('get', key .. ':tokens')) or capacity " +
                      "local last_refreshed = tonumber(redis.call('get', key .. ':timestamp')) or now " +
                      "local delta = math.max(0, now - last_refreshed) * rate / 1000 " +
                      "local filled_tokens = math.min(capacity, last_tokens + delta) " +
                      "if filled_tokens < requested then " +
                      "    return {0, filled_tokens} " +
                      "else " +
                      "    filled_tokens = filled_tokens - requested " +
                      "    redis.call('set', key .. ':tokens', filled_tokens) " +
                      "    redis.call('set', key .. ':timestamp', now) " +
                      "    return {1, filled_tokens} " +
                      "end";
    }

    public boolean acquireToken(String key, int rate, int capacity, int requested) {
        try (Jedis jedis = jedisPool.getResource()) {
            long now = System.currentTimeMillis();
            Object result = jedis.eval(script, 1, key, String.valueOf(rate), String.valueOf(capacity), String.valueOf(now), String.valueOf(requested));
            List<Long> results = (List<Long>) result;
            return results.get(0) == 1;
        }
    }
}

使用示例

public class Main {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("localhost", 6379);
        TokenBucket tokenBucket = new TokenBucket(jedisPool);

        String key = "my_rate_limiter";
        int rate = 10; // 每秒生成10个令牌
        int capacity = 100; // 令牌桶的容量
        int requested = 1; // 每次请求消耗1个令牌

        boolean allowed = tokenBucket.acquireToken(key, rate, capacity, requested);
        if (allowed) {
            System.out.println("Request allowed");
        } else {
            System.out.println("Request denied");
        }

        jedisPool.close();
    }
}
Redis令牌桶限流是一种常见的限流算法,它基于令牌桶算法实现。根据引用\[1\]中的代码,可以看出Redis令牌桶限流需要一个ScheduledThread来定时放入令牌。具体实现是通过使用ScheduledThreadExecutor.scheduleAtFixedRate方法,在规定的时间间隔内放入令牌到令牌桶中。 与漏桶算法相比,令牌桶算法允许一定程度的突发情况,同时可以方便地改变速率。引用\[2\]中提到,令牌桶算法可以根据需要提高放入桶中令牌的速率,从而提高限流速度。因此,令牌桶算法限流框架中的核心算法。 在使用Redis令牌桶限流时,可以通过调用相关方法来获取令牌。引用\[3\]中的代码示例展示了如何使用Redis令牌桶限流来限制上传操作的频率。在该示例中,通过调用redisRaterLimiter.acquireToken方法来获取令牌,如果获取到令牌则可以进行上传操作,否则会抛出限流异常。 综上所述,Redis令牌桶限流是一种基于令牌桶算法实现的限流方法,它可以通过定时放入令牌来控制请求的频率,并允许一定程度的突发情况。 #### 引用[.reference_title] - *1* *2* *3* [分布式限流实战--redis实现令牌桶限流](https://blog.csdn.net/u011296165/article/details/107761489)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值