Redis集群限流必须在客户端或代理层实现,因协议层无内置支持;需避免节点级限流导致计数不一致,推荐用proxy或SDK集成限流,结合client_ip:app_id:key_prefix维度,并以Redis Cluster自身存储令牌桶状态。

Redis集群本身不支持动态限流,必须靠客户端或代理层实现
Redis Cluster 协议层没有任何限流能力,CLUSTER SLOTS、ASK、MOVED 响应里不携带速率控制字段。所有“集群限流”需求,本质是把限流逻辑前置到请求入口——要么改客户端 SDK,要么加一层代理。直接在 Redis 节点上用 redis-cell 或 LUA + INCR 做限流,会因哈希槽分散导致计数不一致,且无法跨 slot 聚合统计。
用 redis-proxy(如 twemproxy / redis-shake / 自研 proxy)做集群代理时,限流必须绑定 client IP + key pattern
代理层限流若只按原始命令频次(如每秒最多 100 个 GET),容易误杀正常批量请求;若只按 client IP,又无法区分关键 key 和非关键 key。真实场景中,应组合使用:
-
client_ip:app_id:key_prefix作为限流维度,例如10.20.30.40:order-service:order: - 限流策略配置在 proxy 启动参数或热加载配置文件中,避免每次请求都查外部存储
- 注意 proxy 的连接复用机制:HTTP/2 或长连接下,单个连接可能承载多个业务方流量,需从
X-Real-IP或 TLS SNI 提取真实标识 - twemproxy 不支持运行时插件,必须用 fork 版本(如
nutcracker-lua)或换用支持 Lua 的redis-shakeproxy 模式
客户端 SDK 自集成限流时,警惕 pipeline 和 multi-exec 中的漏放行
Java 客户端如 Lettuce 或 Jedis,在开启 pipeline 或 multi/exec 后,整个批次只触发一次限流检查,但实际可能执行数十个命令。这会导致突增流量绕过限制。正确做法是:
- 在
CommandArgs构造阶段就对每个子命令提取 key,做预判限流(Lettuce 可用CommandWrapper拦截) - 对
EVAL类命令,必须解析 LUA 脚本内容提取 keys,不能只看第一个参数(Redis Cluster 要求 keys 必须在同一 slot) - 限流器状态(如令牌桶)不能存在本地内存,得用分布式存储;但为避免引入新依赖,可用 Redis Cluster 自身存 token 数,key 设计为
rate:ip:prefix:timestamp,TTL 设为滑动窗口长度
高并发突增时,proxy 或客户端限流的 fallback 必须降级到 connection-level 保护
当限流模块自身成为瓶颈(比如 Redis 限流计数慢、proxy CPU 打满),再精细的 key 级限流已无意义。此时要立即启用兜底措施:
- proxy 层设置最大并发连接数(
max_clients)和每 IP 连接上限(max_client_connections_per_ip) - 客户端 SDK 配置连接池硬限制:
maxTotal=200、maxIdle=50,并开启blockWhenExhausted=true阻塞而非抛异常 - 拒绝响应返回标准错误码(如 HTTP 429)和可读提示,例如
"ERR rate_limit_exceeded key=order:123456",方便上游快速识别并重试或熔断
真正难的不是写限流逻辑,而是确认哪一层丢请求代价最小——proxy 丢请求影响全局,客户端丢请求只影响单实例,而 Redis 内核丢请求(如 OOM kill)会导致数据不一致。别让限流器比业务还脆弱。


















