双十一零点扛过10倍流量洪峰:Sentinel与Redis+Lua的分布式限流深度避坑指南
导读双十一零点流量瞬间暴涨 10 倍数据库连接池打满服务超时雪崩一触即发……限流不是可选项是微服务的生命线。今天我们来聊聊如何在分布式环境下优雅地“丢请求”保核心系统。在单体时代限流很简单Guava RateLimiter、Semaphore 或者漏桶/令牌桶算法内存里玩得风生水起。但到了微服务、多实例部署的场景单个节点的限流变成了一本糊涂账——节点 A 限制 100 QPS节点 B 也限制 100 QPS但总流量可能冲到 200 QPS后端依然被打爆。分布式限流登场将限流状态集中管理Redis、Sentinel 集群所有节点共享同一个“流量额度”真正实现对全局入口的精准控制。本文将从原理、方案对比、代码实战到避坑点全面剖析企业级分布式限流的两种主流路子——Redis Lua和阿里 Sentinel。一、从“单机兵”到“集团军”为什么需要分布式限流先看一个典型反例订单服务部署了 5 个 Pod单机限流 200 QPS令牌桶总容量 5 × 200 1000 QPS外部恶意流量 1500 QPS分摊到每台 300 QPS每台都超限但单机限流各自为政总流量依然冲垮了下游数据库分布式限流的核心目标所有节点共享同一个限流计数器/令牌桶/漏桶状态实现全局公平或按资源的精细化控制。二、四大限流算法快速复习半分钟看懂算法原理特点适用场景计数器窗口内累加请求数超阈值拒绝简单有“突刺”问题粗粒度、非敏感场景滑动窗口将窗口分多个小格子滑动计数平滑内存占用稍高通用 API 限流漏桶请求入桶恒定速率流出强行平滑突发流量保护下游弱处理能力令牌桶以固定速率放令牌请求拿令牌通行允许短时突发灵活高性能、允许突增场景分布式限流多采用滑动窗口或令牌桶的高性能实现。三、两大主流方案对比RedisLua vs Sentinel维度Redis Lua阿里 Sentinel集群流控核心原理Lua 脚本原子性操作 Redis 计数器/令牌桶基于滑动窗口 集群 Token Server依赖组件Redis必须可独立集群模式需 Token Server 或 Redis性能单次 Redis 调用 ~0.1ms超高并发会拉高延迟本地限流几乎无损耗集群限流需 RPC 调用准确性强一致Redis 单机/集群保证原子性集群模式存在少量误差Netty 通信延迟功能丰富度需手写算法逻辑开箱即用QPS/线程数/冷启动/关联限流/热点参数限流运维成本低Redis 已是标配中等Sentinel 控制台需额外部署语言无关性任何语言都能用Java 最佳非 Java 需自研客户端流量分摊全局精确支持按调用来源、任意标识分组一句话总结RedisLua轻量、通用、灵活适合 Redis 已有、不想引入新组件的中小型团队。Sentinel功能全、生态好Spring Cloud Alibaba 亲儿子适合 Java 栈、需要复杂流控策略熔断、热点、系统自适应的大中型项目。四、实战一Redis Lua 实现分布式令牌桶限流本实战将实现一个全局限流注解任何方法加上RedisRateLimiter即可保护。4.1 为什么必须用 Lua 脚本因为 Redis 的INCR、GET、SET等命令不是原子组合。令牌桶需要“获取令牌 更新剩余令牌”两步高并发下会出现超卖。Lua 脚本在 Redis 中整体原子执行完美解决。4.2 编写 Lua 脚本token_bucket.lua-- 令牌桶限流 Lua 脚本-- KEYS[1] : 桶的唯一 key-- ARGV[1] : 最大令牌容量 capacity-- ARGV[2] : 令牌生成速率 rate (每秒几个)-- ARGV[3] : 当前请求需要的令牌数 (通常为1)-- ARGV[4] : 当前时间戳(秒)localkeyKEYS[1]localcapacitytonumber(ARGV[1])localratetonumber(ARGV[2])localrequestedtonumber(ARGV[3])localnowtonumber(ARGV[4])-- 获取桶的当前状态 {last_refresh_time, current_tokens}localbucketredis.call(hmget,key,last_time,tokens)locallast_timetonumber(bucket[1])ornowlocaltokenstonumber(bucket[2])orcapacity-- 计算应该补充的令牌数localdeltamath.max(0,now-last_time)localfilled_tokensmath.min(capacity,tokens(delta*rate))-- 判断是否足够localallowed0iffilled_tokensrequestedthenallowed1filled_tokensfilled_tokens-requestedend-- 保存新状态redis.call(hmset,key,last_time,now,tokens,filled_tokens)-- 设置过期时间避免闲置 key 浪费内存2倍时间窗口redis.call(expire,key,60)returnallowed4.3 Spring Boot 中集成 Redis Lua① 引入依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId/dependency② 加载 Lua 脚本ComponentpublicclassRedisRateLimiter{privatefinalRedisScriptLongrateLimitScript;publicRedisRateLimiter(RedisTemplateString,ObjectredisTemplate){// 读取 classpath 下的 lua 文件DefaultRedisScriptLongscriptnewDefaultRedisScript();script.setScriptSource(newResourceScriptSource(newClassPathResource(lua/token_bucket.lua)));script.setResultType(Long.class);this.rateLimitScriptscript;this.redisTemplateredisTemplate;}AutowiredprivateRedisTemplateString,ObjectredisTemplate;publicbooleantryAcquire(Stringkey,longcapacity,doublerate,intrequested){ListStringkeysCollections.singletonList(key);LongresultredisTemplate.execute(rateLimitScript,keys,String.valueOf(capacity),String.valueOf(rate),String.valueOf(requested),String.valueOf(System.currentTimeMillis()/1000));returnresult!nullresult1L;}}③ 自定义注解 AOP 实现无侵入限流Target({ElementType.METHOD})Retention(RetentionPolicy.RUNTIME)publicinterfaceDistributedRateLimit{Stringkey();// 限流 key支持 SpELlongcapacity()default100;doublerate()default10;// 每秒生成令牌数intrequested()default1;}切面实现省略部分校验代码Around(annotation(rateLimit))publicObjectaround(ProceedingJoinPointjoinPoint,DistributedRateLimitrateLimit)throwsThrowable{StringkeyparseKey(rateLimit.key(),joinPoint);// 支持 SpEL 解析如 #userIdbooleanallowedredisRateLimiter.tryAcquire(key,rateLimit.capacity(),rateLimit.rate(),rateLimit.requested());if(!allowed){thrownewRateLimitException(请求过于频繁请稍后再试);}returnjoinPoint.proceed();}④ 业务处使用GetMapping(/order)DistributedRateLimit(keyorder:create:#{#userId},capacity50,rate10)publicStringcreateOrder(RequestParamLonguserId){return订单创建成功;}效果无论多少个实例同一个userId的请求被全局限制在 10 QPS且允许短时突发。五、实战二阿里 Sentinel 集群流控更适合生产Sentinel 提供两种集群模式嵌入模式Embedded选一个节点作为 Token Server其他为 Client适合小规模独立模式Alone独立 Token Server 集群适合大规模5.1 搭建 Sentinel 控制台dockerrun-d--namesentinel-p8858:8858-p8719:8719 bladex/sentinel-dashboard:1.8.6访问http://localhost:8858默认账号 sentinel/sentinel。5.2 Spring Boot 集成 Sentinel① 依赖dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId/dependencydependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-cluster-client-default/artifactId/dependencydependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-cluster-server-default/artifactId/dependency② 配置集群流控独立模式示例在 application.yml 中配置应用为 Token Clientspring:cloud:sentinel:transport:dashboard:localhost:8858cluster-client:server-host:your-token-server-ip# Token Server 地址server-port:18730flow:cold-factor:3同时在控制台配置集群流控规则如针对/order/create资源的全局 QPS 阈值 500。③ 代码中无需显式调用Sentinel 会通过拦截器自动保护 Web 接口。5.3 Sentinel 比 RedisLua 强在哪里热点参数限流对userId维度单独限流如每个用户 10 QPS普通 Redis 脚本需要为每个用户维护桶可能产生海量 key。熔断降级错误率超过阈值自动熔断半开探测恢复。系统自适应保护根据 CPU 负载、平均 RT 等自动调整入口流量。动态规则推送通过 Nacos/Apollo 实现规则热更新。六、生产避坑指南重点6.1 Redis 单点故障与网络开销问题每次限流都要请求 Redis网络 RTT 增加 ~0.5ms高并发下 Redis 成为瓶颈。解法使用 Redis Cluster 做高可用。本地缓存配合批量模式如每次请求拿 5 个令牌消耗完再请求 Redis。降级方案Redis 不可用时切换到单机限流Guava RateLimiter并打印告警。6.2 Sentinel 集群模式的偏差由于 Token Server 和 Client 之间通信是异步 Netty存在毫秒级延迟在严格秒杀场景下可能累计误差。建议对极端精确场景使用 RedisLua 并压测验证精度。6.3 限流 Key 的设计与热点问题不合理limiter:user所有用户共享一个桶→ 一个恶意用户能刷爆全局限流。合理limiter:user:{userId}按用户隔离但要警惕海量用户时 Redis 内存爆炸。解决方案对用户限流可采用滑动窗口 布隆过滤器仅对活跃用户动态创建 key并设置 TTL。6.4 限流后用户体验优化返回明确的限流错误码如 429和 Retry-After 头部。实现分级限流VIP 用户阈值更高普通用户阈值更低。异步排队对于写请求限流后可放入队列稍后处理而不是粗暴拒绝。七、方案选型速查表你的情况推荐方案Redis 已部署团队小需要快速实现全局限流Redis Lua已有 Sentinel 生态Spring Cloud Alibaba需要熔断、热点、系统自适应Sentinel 集群流控非 Java 栈Go/Python统一流量治理Redis Lua或基于 Envoy 的全局限流RLS超高并发10万 QPS要求极致性能Netflix Concurrency Limits极限并发控制 本地滑动窗口结合 Redis 做周期性同步八、总结分布式限流不是单一的算法或组件而是一套根据业务场景权衡的“流量手术刀”。本文我们从原理切入对比了 RedisLua 和 Sentinel 两种主流方案并分别给出了完整的 Spring Boot 实战代码以及生产中那些容易踩的坑。RedisLua造轮子成本低适合通用、灵活动态控制的场景。Sentinel功能航母适合 Java 生态的复杂治理需求。记住限流的本质是延迟拒绝而不是系统崩溃。优雅地丢请求比让用户等到超时更尊重用户体验。最后无论你选择哪条路压测和监控永远是最好的老师。希望这篇文章能帮你构建起你的第一条“分布式护城河”。 关注《卷毛的技术笔记》专注后端硬核技术分享拒绝套路只聊落地的技术。如果觉得文章对你有帮助欢迎点赞、收藏、关注