问题:如果有这样一个场景,对于同一个url请求,1分钟之内请求数达到10次就不允许请求?这样一个业务场景,你应该怎么设计?
这是一个典型的 限流(Rate Limiting) 场景。对于同一个 URL 请求,在 1 分钟之内请求超过 10 次就拒绝访问,属于 基于时间窗口的限流策略。
✅ 一、整体设计思路
我们可以使用 Redis 来实现一个高效的限流器(Rate Limiter),利用其高性能读写和过期机制来记录每个客户端对某个 URL 的访问频率。
1. 基本逻辑
- 对于每次请求,根据客户端标识(如 IP 或用户 ID)+ URL 构造一个 key。
- 使用 Redis 的
INCR
命令对该 key 进行自增计数。 - 如果是第一次访问,则设置该 key 的过期时间为 60 秒。
- 判断当前值是否超过阈值(例如 10),如果超过则拒绝请求。
2. Redis 数据结构选择
- 使用 String 类型 存储访问次数即可。
- Redis 的
INCR
和EXPIRE
命令天然支持原子操作,非常适合这种场景。
✅ 二、Redis Key 设计建议
rate_limit:{client_id}:{url_md5}
示例:
rate_limit:192.168.1.1:/api/user/info
rate_limit:user_123:/api/order/detail
说明:URL 可能比较长,可以使用 MD5 或 SHA1 缩短长度,避免 key 过长影响性能。
✅ 三、具体实现步骤(伪代码)
🧠 核心逻辑(Lua 脚本推荐)
为了保证“判断 + 自增 + 设置过期”的原子性,建议使用 Lua 脚本来完成整个流程:
-- rate_limit.lua
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = tonumber(ARGV[2])
local current = redis.call('get', key)
if current and tonumber(current) >= limit then
return 0 -- 超出限制
elseif current then
redis.call('incr', key)
else
redis.call('setex', key, expire_time, 1)
end
return 1 -- 允许访问
🧪 调用方式(以 Redis CLI 为例)
EVAL "local key=KEYS[1]; local limit=tonumber(ARGV[1]); local expire_time=tonumber(ARGV[2]); local current=redis.call('get', key); if current and tonumber(current)>=limit then return 0; elseif current then redis.call('incr', key); else redis.call('setex', key, expire_time, 1); end; return 1;" 1 rate_limit:192.168.1.1:/api/user/info 10 60
返回值含义:
1
:允许访问0
:超出限制,拒绝访问
✅ 四、系统集成建议
1. 在网关层或中间件中拦截请求
- 在 Nginx/OpenResty、Spring Cloud Gateway、Zuul、Kong 等网关中嵌入限流逻辑。
- 可以结合 Lua 脚本直接调用 Redis 实现高效限流。
2. 客户端标识建议
- IP 地址:适用于无登录态的公共接口。
- 用户ID(uid):适用于已登录用户。
- API Key / Token:更精确控制访问权限。
3. 日志与监控
- 记录被限流的请求日志。
- 结合 Prometheus + Grafana 监控限流情况。
- 配置告警机制,防止恶意攻击。
✅ 五、扩展功能(可选)
功能 | 描述 |
---|---|
滑动窗口限流 | 更精确地统计最近 60 秒内的请求数(比固定窗口更平滑) |
动态配置限流规则 | 支持不同用户/角色有不同的限流阈值 |
黑名单机制 | 多次触发限流的用户加入黑名单 |
集群限流 | 多节点共享限流状态,适合微服务架构 |
✅ 六、注意事项
问题 | 解决方案 |
---|---|
Redis 单点故障 | 使用 Redis Cluster 或哨兵模式保障高可用 |
键名冲突 | 加命名空间前缀,如 ratelimit: |
内存占用过高 | 合理设置 TTL,定期清理冷数据 |
Lua 脚本执行超时 | 控制脚本复杂度,不要做太多计算 |
误限流 | 区分真实用户和爬虫,必要时增加 CAPTCHA 验证 |
✅ 七、总结
项目 | 推荐方案 |
---|---|
数据存储 | Redis String |
原子操作 | Lua 脚本封装 INCR + EXPIRE |
Key 设计 | rate_limit:{client_id}:{url} |
部署位置 | API 网关或业务层前置中间件 |
扩展能力 | 支持滑动窗口、黑名单、动态规则等 |