使用Java实现Redis限流的方法
限流的核心是在单位时间内限制模块接收的最大请求数。本文将详细演示如何基于Java、Jedis客户端和Redis的ZSet数据结构,实现一个滑动窗口的限流逻辑。
限流原理与Redis ZSet应用
限流旨在防止短时间内的过高并发导致系统不可用。例如在秒杀场景中,需确保特定时间段内发往支付模块的请求不超过上限。Redis中的有序集合(Sorted Set,简称ZSet)是实现这一机制的理想选择。
ZSet中的每个元素都关联一个分数(score),该分数可用于排序。我们可以利用这一特性:
- 将当前时间戳作为score,保证数据的时序性和可排序性。
- 利用 ZADD 命令添加请求记录。
- 利用 ZREMRANGEBYSCORE 命令删除指定时间范围外的旧数据。
- 利用 ZCARD 命令统计当前时间窗口内的有效请求数量。
整体流程如下:
1. 获取当前时间戳。
2. 向ZSet中添加请求ID(值唯一,避免重复),分数为当前时间戳。
3. 清除早于“当前时间 - 时间窗口”的所有记录。
4. 计算集合大小,判断是否超过限流阈值。
环境准备与容器配置
本例使用Docker创建的Redis容器作为后端存储。确保Redis服务已启动并可通过网络访问。
Java代码实现步骤
第1步:定义限流工具类 LimitUtil
目的:封装核心限流逻辑,隔离业务与Redis交互细节。
入口:静态方法 canVisit(Jedis jedis, String requestType, int limitTime, int limitNum)
动作:
- 获取
currentTime。 - 调用
jedis.zadd()存入当前时间戳作为Value和Score。 - 调用
jedis.zremrangeByScore()移除[0, currentTime - limitTime*1000]范围内的过期记录。 - 调用
jedis.zcard()获取剩余元素数量。 - 设置键的过期时间
expire(requestType, limitTime + 1),防止内存泄漏。 - 比较
limitNum与当前数量,输出允许或拒绝状态。
class LimitUtil {
// 判断是否需要限流
public static void canVisit(Jedis jedis, String requestType, int limitTime, int limitNum) {
long currentTime = System.currentTimeMillis();
// 1. 把请求放入zset,Value保证唯一性,Score用于时间排序
jedis.zadd(requestType, currentTime, Long.valueOf(currentTime).toString());
// 2. 去掉时间范围外(超时)的请求
jedis.zremrangeByScore(requestType, 0, currentTime - limitTime * 1000);
// 3. 统计时间范围内总数
Long count = jedis.zcard(requestType);
// 4. 设置所有请求的超时时间,自动清理残留数据
jedis.expire(requestType, limitTime + 1);
boolean flag = limitNum >= count;
if (flag) {
System.out.println("Can visit");
} else {
System.out.println("Can not visit");
}
}
}
第2步:编写测试主类 LimitRequest
目的:模拟并发请求场景,验证限流效果。
参数说明:
- 连接地址:
192.168.159.33,端口:6379。 - 限流规则:100秒内只允许3个请求(
limitTime=100,limitNum=3)。 - 模拟操作:循环发送5次请求。
package com.baizhi.request;
import redis.clients.jedis.Jedis;
public class LimitRequest {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.159.33", 6379);
// 每次运行前清空数据,避免历史数据干扰测试
jedis.del("PayRequest");
// 模拟发5个请求
int cnt = 5;
for (int i = 0; i < cnt; i++) {
LimitUtil.canVisit(jedis, "PayRequest", 100, 3);
}
jedis.close();
}
}
结果观察与分析
执行上述程序后,由于限流阈值为3,而实际发起了5个请求,前3个请求将被允许(输出 Can visit),第4和第5个请求因超出时间窗口内的计数限制而被拒绝(输出 Can not visit)。
注意:虽然 zremrangeByScore 会物理删除过期数据,但主动设置 expire 是良好的实践,确保即使在极端并发下未成功执行的清理逻辑也能随键的销毁而释放内存。
以上就是Java实现Redis限流的详细内容,更多关于Java Redis限流优化的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









