本文目录导读:

我来详细介绍Redis实现滑动窗口限流算法的多种方法:
基于ZSET的滑动窗口实现
这是最常见的实现方式,利用有序集合的时间戳特性。
核心实现
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
public class SlidingWindowRateLimiter {
private Jedis jedis;
private String prefix = "rate:limiter:";
public SlidingWindowRateLimiter(Jedis jedis) {
this.jedis = jedis;
}
/**
* 滑动窗口限流
* @param key 限流key
* @param windowSize 窗口大小(秒)
* @param maxRequests 最大请求数
* @return 是否允许通过
*/
public boolean allowRequest(String key, int windowSize, int maxRequests) {
String windowKey = prefix + key;
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - windowSize * 1000L;
// 使用Pipeline批量操作
Pipeline pipeline = jedis.pipelined();
// 1. 移除窗口外的数据
pipeline.zremrangeByScore(windowKey, 0, windowStart);
// 2. 添加当前请求
pipeline.zadd(windowKey, currentTime, String.valueOf(currentTime));
// 3. 统计窗口内的请求数
Response<Long> count = pipeline.zcard(windowKey);
// 4. 设置过期时间
pipeline.expire(windowKey, windowSize + 1);
pipeline.sync();
// 检查是否超过限制
return count.get() <= maxRequests;
}
}
Lua脚本实现(原子性操作)
-- 滑动窗口限流Lua脚本
-- KEYS[1]: 限流key
-- ARGV[1]: 窗口大小(秒)
-- ARGV[2]: 最大请求数
local windowKey = KEYS[1]
local windowSize = tonumber(ARGV[1])
local maxRequests = tonumber(ARGV[2])
local currentTime = redis.call('TIME')[1] * 1000 -- 获取Redis时间,转为毫秒
local windowStart = currentTime - windowSize * 1000
-- 1. 移除过期数据
redis.call('ZREMRANGEBYSCORE', windowKey, 0, windowStart)
-- 2. 获取当前请求数
local currentCount = redis.call('ZCARD', windowKey)
-- 3. 判断是否限流
if currentCount >= maxRequests then
return 0 -- 限流
end
-- 4. 允许请求,添加记录
redis.call('ZADD', windowKey, currentTime, currentTime)
redis.call('EXPIRE', windowKey, windowSize + 1)
return 1 -- 通过
Java调用Lua脚本:
public class LuaSlidingWindowRateLimiter {
private Jedis jedis;
private String luaScript;
public LuaSlidingWindowRateLimiter(Jedis jedis) {
this.jedis = jedis;
// 加载Lua脚本
this.luaScript = loadLuaScript();
}
public boolean allowRequest(String key, int windowSize, int maxRequests) {
String scriptSHA = jedis.scriptLoad(luaScript);
Long result = (Long) jedis.evalsha(
scriptSHA,
1, // 1个key
key,
String.valueOf(windowSize),
String.valueOf(maxRequests)
);
return result == 1L;
}
}
基于LIST的实现
使用List存储请求时间戳,实现更轻量级的滑动窗口。
public class ListBasedRateLimiter {
private Jedis jedis;
public boolean allowRequest(String key, int windowSize, int maxRequests) {
String windowKey = "rate:list:" + key;
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - windowSize * 1000L;
Pipeline pipeline = jedis.pipelined();
// 移除过期请求
while (true) {
String oldest = jedis.lindex(windowKey, -1);
if (oldest == null || Long.parseLong(oldest) > windowStart) {
break;
}
jedis.rpop(windowKey);
}
// 检查当前数量
long currentCount = jedis.llen(windowKey);
if (currentCount >= maxRequests) {
return false;
}
// 添加新请求(头部)
jedis.lpush(windowKey, String.valueOf(currentTime));
jedis.expire(windowKey, windowSize + 1);
return true;
}
}
基于RedisTimeSeries模块
如果Redis安装了TimeSeries模块,可以使用更专业的时间序列功能。
// 需要RedisTimeSeries模块支持
public class TimeSeriesRateLimiter {
private Jedis jedis;
public void createRule(String key, int windowSize, int maxRequests) {
// 创建时间序列
jedis.sendCommand(
Protocol.Command.valueOf("TS.CREATE"),
"ts:" + key,
"RETENTION", String.valueOf(windowSize * 1000), // 保留时间
"LABELS", "type", "rate_limit"
);
}
public boolean allowRequest(String key, int maxRequests) {
long currentTime = System.currentTimeMillis() * 1000; // 微秒
// 添加时间戳
jedis.sendCommand(
Protocol.Command.valueOf("TS.ADD"),
"ts:" + key,
String.valueOf(currentTime),
"1" // 值
);
// 查询窗口内的请求数
long windowStart = currentTime - windowSize * 1000 * 1000; // 微秒
Object result = jedis.sendCommand(
Protocol.Command.valueOf("TS.RANGE"),
"ts:" + key,
String.valueOf(windowStart),
String.valueOf(currentTime)
);
// 解析结果,统计请求数
return parseCount(result) <= maxRequests;
}
}
多维度限流实现
支持不同粒度的限流(秒级、分钟级、小时级)。
public class MultiDimensionRateLimiter {
private Jedis jedis;
public boolean allowRequest(String userId, String apiPath) {
// 多维度限流规则
Map<String, Integer> rules = new HashMap<>();
rules.put("second", 10); // 每秒10次
rules.put("minute", 100); // 每分钟100次
rules.put("hour", 1000); // 每小时1000次
for (Map.Entry<String, Integer> rule : rules.entrySet()) {
String windowKey = getWindowKey(userId, apiPath, rule.getKey());
int windowSize = getWindowSize(rule.getKey());
if (!checkWindow(windowKey, windowSize, rule.getValue())) {
return false; // 任意维度超限则拒绝
}
}
return true;
}
private String getWindowKey(String userId, String apiPath, String dimension) {
return String.format("rate:%s:%s:%s", userId, apiPath, dimension);
}
private int getWindowSize(String dimension) {
switch (dimension) {
case "second": return 1;
case "minute": return 60;
case "hour": return 3600;
default: return 60;
}
}
}
优化和注意事项
内存优化版本
public class OptimizedRateLimiter {
// 使用HyperLogLog减少内存占用
public boolean allowRequestHLL(String key, int windowSize, int maxRequests) {
String hllKey = "rate:hll:" + key + ":" + getWindowSlot();
jedis.pfadd(hllKey, String.valueOf(System.nanoTime()));
long count = jedis.pfcount(hllKey);
return count <= maxRequests;
}
private String getWindowSlot() {
long currentTime = System.currentTimeMillis();
// 将时间划分为固定窗口
return String.valueOf(currentTime / 10000); // 10秒一个槽
}
}
分布式环境下的实现
public class DistributedRateLimiter {
private RedissonClient redisson;
public boolean allowRequest(String key, int limit, int windowSize) {
RRateLimiter rateLimiter = redisson.getRateLimiter(key);
// 设置速率:limit次/windowSize秒
rateLimiter.trySetRate(
RateType.OVERALL,
limit,
windowSize,
RateIntervalUnit.SECONDS
);
return rateLimiter.tryAcquire();
}
}
完整的使用示例
public class RateLimiterDemo {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
SlidingWindowRateLimiter limiter = new SlidingWindowRateLimiter(jedis);
// 模拟请求
String userId = "user:123";
int windowSize = 60; // 60秒窗口
int maxRequests = 10; // 最多10次请求
for (int i = 0; i < 20; i++) {
boolean allowed = limiter.allowRequest(userId, windowSize, maxRequests);
System.out.println("Request " + (i + 1) + ": " + (allowed ? "ALLOWED" : "REJECTED"));
try {
Thread.sleep(1000); // 间隔1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
jedis.close();
}
}
滑动窗口限流的核心是:
- 数据结构:ZSET是最常用的,支持按分数(时间戳)范围查询和删除
- 原子性:使用Lua脚本确保操作的原子性
- 内存管理:及时清理过期数据,设置合理的TTL
- 性能优化:使用Pipeline减少网络开销
选择哪种实现取决于你的具体需求:
- ZSET方案:最灵活,适合大多数场景
- List方案:简单轻量,适合请求量不大的场景
- TimeSeries方案:专业的时间序列处理,需要额外模块
- 多维度方案:需要复杂的限流策略时使用
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播