[特殊字符] Redis + Lua 实现令牌桶限流器实践经验分享

一、📌 背景介绍

在高并发场景下,为防止服务被短时间内的大量请求压垮,我们需要限流机制来控制访问速率。令牌桶(Token Bucket)是其中一种常见的限流算法,它相比漏桶,更灵活地支持突发请求

结合 Redis 的原子操作和高性能,我们可以使用 Redis + Lua 脚本 高效地实现分布式令牌桶限流。


二、🧠 令牌桶原理回顾

  • 系统以固定速率向桶中加入令牌(如每秒 10 个);

  • 桶有最大容量,超出不再添加;

  • 每次请求需要取一个令牌,没有令牌则被限流;

  • 可允许一定程度的突发请求(桶中积累的令牌)。


三、🔧 Redis 实现令牌桶的关键点

我们需要用 Redis 存储以下几个信息:

Key描述
tokens当前令牌数
last_time上次填充令牌的时间戳

每次请求时,我们需要做以下操作:

  1. 计算当前时间距离上次填充的时间差;

  2. 计算应该补充的令牌数;

  3. 判断当前令牌是否足够;

  4. 修改 tokenslast_time


四、📜 Lua 脚本实现

-- redis-limiter.lua
local key = KEYS[1]               -- 令牌桶的key
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("HGET", key, "tokens")) or capacity
local last_time = tonumber(redis.call("HGET", key, "last_time")) or now

-- 计算可新增令牌数
local delta = math.max(0, now - last_time)
local new_tokens = math.min(capacity, last_tokens + delta * rate)

-- 判断是否能放行
local allowed = new_tokens >= requested
local left_tokens = new_tokens

if allowed then
    left_tokens = new_tokens - requested
end

-- 写入 Redis
redis.call("HSET", key, "tokens", left_tokens)
redis.call("HSET", key, "last_time", now)

return allowed

五、☕ Java 实现调用

public class RedisRateLimiter {
    private final RedisTemplate<String, String> redisTemplate;
    private final DefaultRedisScript<Long> script;

    public RedisRateLimiter(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.script = new DefaultRedisScript<>();
        this.script.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis-limiter.lua")));
        this.script.setResultType(Long.class);
    }

    public boolean tryAcquire(String key, int rate, int capacity, int requestedTokens) {
        long now = System.currentTimeMillis() / 1000;
        List<String> keys = Collections.singletonList(key);
        Long result = redisTemplate.execute(
                script,
                keys,
                String.valueOf(rate),
                String.valueOf(capacity),
                String.valueOf(now),
                String.valueOf(requestedTokens)
        );
        return result != null && result == 1;
    }
}

六、📌 实战场景应用

✅ API 网关限流

  • 对 IP、用户 ID 进行限速;

  • 防止恶意刷接口。

✅ 秒杀系统防刷

  • 限制用户操作频率,保护数据库;

✅ 后台任务控制并发量

  • 比如 Elasticsearch/Bulk 写入等;


七、🚨 注意事项

问题建议
时间不一致由应用传入时间戳,避免 Redis 系统时间差异
分布式环境Redis 天然支持分布式,限流全局有效
精度问题秒级已满足大部分需求,如需更高精度可换为毫秒实现

八、📚 总结

使用 Redis + Lua 实现的令牌桶限流器具备:

✅ 高性能
✅ 原子性操作
✅ 跨服务共享限流状态
✅ 易扩展、可观测性强

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值