一、限流场景与算法选择
1.1 为什么需要分布式限流
在高并发系统中,API接口的突发流量可能导致服务雪崩。传统的单机限流方案在分布式环境下存在局限,需要借助Redis等中间件实现集群级流量控制。
1.2 令牌桶算法优势
- 允许突发流量:稳定速率填充令牌,应对合理流量峰值
- 平滑限流:相比固定窗口算法更细腻的流量控制
- 弹性调整:动态修改令牌生成速率和桶容量
二、Redis核心实现原理
2.1 数据结构设计
Key: rate_limiter:{service_name}
Value:
{
"tokens": 10, // 当前令牌数
"last_time": 1717024000 // 最后更新时间(秒级时间戳)
}
2.2 原子操作保障
使用Redis的Lua脚本保证操作的原子性:
local key = KEYS[1]
local now = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local rate = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
local data = redis.call("HMGET", key, "tokens", "last_time")
local tokens = tonumber(data[1]) or capacity
local last_time = tonumber(data[2]) or now
local delta = math.floor((now - last_time) * rate)
if delta > 0 then
tokens = math.min(tokens + delta, capacity)
last_time = now
end
local result = 0
if tokens >= requested then
tokens = tokens - requested
result = 1
end
redis.call("HMSET", key, "tokens", tokens, "last_time", last_time)
redis.call("EXPIRE", key, 86400) // 自动过期清理
return result
三、Java完整实现代码
3.1 添加依赖(pom.xml)
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.6</version>
</dependency>
3.2 Redis限流器核心类
public class RedisRateLimiter {
private final JedisPool jedisPool;
private final String serviceKey;
private final int capacity; // 桶容量
private final double rate; // 令牌/秒
private static final String LUA_SCRIPT = "..." // 上述Lua脚本
public boolean tryAcquire(int permits) {
try (Jedis jedis = jedisPool.getResource()) {
long now = Instant.now().getEpochSecond();
Object result = jedis.eval(
LUA_SCRIPT,
Collections.singletonList(serviceKey),
Arrays.asList(
String.valueOf(now),
String.valueOf(capacity),
String.valueOf(rate),
String.valueOf(permits)
));
return "1".equals(result.toString());
}
}
}
3.3 使用示例
// 初始化限流器(每秒2个令牌,桶容量10)
RedisRateLimiter limiter = new RedisRateLimiter(jedisPool, "order_api", 10, 2.0);
public Response processRequest(Request request) {
if (!limiter.tryAcquire(1)) {
throw new RateLimitExceededException("请求过于频繁");
}
// 处理业务逻辑
return doBusiness(request);
}
四、高级优化策略
4.1 预热机制
// 冷启动时渐进式填充令牌
private void warmUp() {
long warmupPeriod = 30_000; // 30秒预热期
double coldFactor = 3; // 冷启动系数
double warmupRate = capacity * coldFactor / (warmupPeriod/1000);
// 动态调整rate参数...
}
4.2 动态规则配置
// 监听配置中心变更
@NacosConfigListener(dataId = "rate_limit_rules")
public void updateRules(String config) {
// 解析JSON配置并更新capacity/rate
}
4.3 多维度限流
-- 在Lua脚本中增加维度参数
local key = "rate_limiter:" .. service_name .. ":" .. user_id
五、生产环境注意事项
-
Redis集群模式:使用Hash Tag确保相同资源的请求路由到同一节点
String serviceKey = "{order_api}:" + userId;
-
性能监控:
redis-cli info stats | grep total_commands_processed
-
降级策略:
- 快速失败模式(默认)
- 排队等待模式(配合阻塞队列)
- 动态降级(根据系统负载自动调整rate)
-
异常处理:
try { return tryAcquire(permits); } catch (JedisException e) { // Redis不可用时降级为本地限流 return localLimiter.tryAcquire(); }
总结
本文实现的Redis分布式限流方案已在多个生产环境验证,支撑百万级QPS的电商系统。建议在实际使用中结合监控告警系统,并建立限流规则评审机制,避免因错误配置影响正常业务。