市场部买下一块开屏广告,却忘了通知技术团队。五倍流量同时抵达时,你第一次必须主动拒绝用户——不是因为系统不想服务,而是因为核心链路要活着服务。
创业第 1 年 9 个月,「秒抢」累计用户约 50 万。公司 70 人,后端 14 人。周二下午你正在会议室做迭代评审,产品 Mia 的手机弹出市场投放截图:开屏广告已上线。她刚说完「他们好像没走技术评审」,大屏上的入口曲线就竖了起来。
阿凯在群里先吼了一句:「五倍了!」你把请求按业务拆开:流量最大的不是下单,而是广告落地页带来的推荐、榜单和历史订单查询。核心交易只占一小部分,却正和这些可以晚点看的请求抢线程、连接和缓存带宽。
扩容来不及,60,800 也不会因为你盯着大盘就变成 15,000。线索指向同一件事:先把进入核心系统的总量封顶,再从配额里优先保住下单、支付与退款;推荐、榜单和历史订单可以返回简化页或稍后重试。
A的问题是时间与瓶颈位置。 拉起实例、预热缓存和建立连接按分钟计,系统按秒恶化;应用扩成五倍也不会让数据库容量同步变成五倍。扩容是恢复容量的后续动作,不是此刻的入口刹车。
B是教科书答案:先选择谁能进入。 15,000 QPS 是压测验证过的安全吞吐,网关先封顶总量,再把配额留给下单、支付、退款。非核心功能返回降级结果,用户会少看到一些内容,但交易链路获得确定的资源边界。
C代价比它看起来大得多。 60,800 QPS 若失败后立即尝试三次,最坏会把重试流量叠到原请求上。第 7 章会完整拆解这类“为了成功而重试,结果把下游压死”的放大链。
D没错,只是它解决的是明天的问题。 若瓶颈真在过小的应用池,适度调整可能有用;但当前到达量已是验证容量约四倍。把并发闸门开大,只会让更多请求同时争抢下游,必须先把总量压住。
限流先回答一个朴素问题:已知系统每秒能安全处理多少,就不让更多请求穿过护栏。到达量低于配额时正常放行;到达量高于配额时,超出的请求必须排队、降级或拒绝。
把现场数字代进去:配额 15,000 QPS,到达 60,800 QPS,实际通过量是 15,000 QPS,约 45,800 QPS 要被拒绝或降级;若投放结束后到达降到 8,000 QPS,则通过 8,000 QPS,不该为了“配额用满”制造额外请求。
「限流不是拒绝用户,是选择先拒绝谁。」如果只按到达顺序随机拒绝,退款可能和推荐页拿到同样待遇;真正的生产限流还要有优先级、租户隔离与保底配额。总量护栏回答“放多少”,业务分层回答“先放谁”。
令牌桶、漏桶和滑动窗口都能执行配额,但对突发的态度不同:
| 算法 | 波形 | 优势 | 账单 |
|---|---|---|---|
| 令牌桶 | 积攒令牌后先放一个高于配额的尖头,再回落 | 允许短突发,平均速率仍受控 | 桶越深,转嫁给下游的瞬时峰值越高 |
| 漏桶 | 有积压时以近似恒定速率流出 | 下游看到平滑直线,容量规划简单 | 突发被改写成等待,队列满后仍要丢弃 |
| 滑动窗口 | 按最近时间窗计数,呈离散台阶响应 | 规则直观,能表达用户或接口维度配额 | 状态与计算成本较高,窗口边界仍需谨慎实现 |
限流只决定流量能否进入,降级决定进入后少做什么。事故中先画出服务层级,能避免每个团队都声称自己的功能“不可关”:
| 层级 | 功能 | 过载动作 | 业务理由 |
|---|---|---|---|
| L0 保住 | 下单、支付、退款 | 保底配额、独立线程池、优先队列 | 直接影响钱与履约,读错或拒绝代价高 |
| L1 简化 | 场次详情、购票资格 | 返回精简字段、使用短时旧值 | 交易需要,但可以少做昂贵拼装 |
| L2 关闭 | 推荐、榜单、历史订单 | 静态兜底、稍后再试或直接隐藏 | 暂时缺席不会破坏正在进行的交易 |
第 5 章 D1 用入口限流打破缓存雪崩的死亡螺旋,本章把那次临时止血制度化。第 12 章会把网关限流放进秒杀漏斗的第二层,让入口洪峰经过静态化、配额和排队后才接近库存。
本章不新增病历卡。限流与降级是给已有故障开出的兜底处方,也是对下一章级联故障的前向设防。
两张形状放在一起看:No.06 是缓存命中率先断崖,No.09 是服务可用性从下游向上游逐层熄灭。限流的价值不是让曲线好看,而是在第一层资源饱和时就结束传播。
把完全相同的到达序列同时注入令牌桶、漏桶和滑动窗口;曲线形状会直接告诉你,每种算法愿意怎样把突发交给下游。
burst×4 的排队容量、滑动窗口固定 10 个桶,都是为了让三条曲线可比而设的参数。模型里这两个数字是常量 → 真实实现的队列容量和窗口粒度是工程可调项,直接决定尾延迟和内存占用 → 因此不要把这里的默认值当成「漏桶就该配 4 倍 burst」的推荐配置。
这些简化是故意的:模拟器的目的是隔离一个因果关系,不是预测生产数值。
实验清单里的每条现象都由 tools/simN-*-model-test.js 的断言保证,
但保证的是这个模型里的现象。
限流放在哪里,决定谁看得到业务语义、谁能执行全局预算。网关擅长统一入口,各服务最清楚自身容量,两级配额把两者叠在一起。三个方案都对,但治理成本与失效方式不同。
A · 网关统一:策略集中、变更快,能按用户、IP、租户做一致配额,流量在消耗内部资源前就被挡住。 账单:网关不知道每个服务的实时瓶颈,规则错误会产生大范围影响;内部调用绕过网关时得不到保护。适合公网入口明确、团队需要快速统一治理的系统。
B · 服务自治:服务最了解线程池、依赖和业务优先级,能按局部健康动态调整。 账单:每个团队重复实现算法与观测,策略口径容易漂移;请求走到服务才被拒绝,前面资源已经花掉。适合依赖差异大、内部调用多、团队自治成熟的系统。
C · 两级限流:网关封住总流量,服务再按自身容量保护最后一层,单个规则失准时仍有第二道护栏。 账单:要协调两层配额、错误码与指标,避免同一请求被重复计费;排障要分清是哪道闸门拒绝。适合核心链路多、需要全局与局部双重保护的系统。
「秒抢」的选择:采用 C。网关按压测容量封住站点总量并执行用户公平性,订单、支付、退款各自设并发上限与保底配额。理由是约 50 万用户阶段流量入口已复杂,单靠网关看不见支付渠道变慢,单靠服务又挡不住广告洪峰在内部消耗连接。
回放 14:05 的正确剧本——市场投放继续,但系统不再试图接住每一个请求:
封顶总量。网关配额设为 15,000 QPS,超额请求返回带退避时间的繁忙页。order-service P99 从 4.8s 降到 1.3s,5xx 停止上升。
按价值降级。关闭推荐和榜单,历史订单返回稍后再试;释放的配额转给下单、支付、退款。核心交易成功率从 76% 回升到 96.8%。
校准配额。观察 DB、缓存和线程池余量,将总配额从 15,000 微调到 16,500 QPS;每次只加 5%,核心 P99 稳在 420ms 内。
恢复层级。流量回落到约 24,000 QPS 后,先恢复历史订单,再恢复榜单;推荐继续用静态兜底。广告没撤,交易链路保持可用。
一条调用链的可用性曲线呈 ███▇▅▃▁:下游先过载,上游随后因等待与资源耗尽逐层熄灭。这最接近哪张病历?
B。逐层熄灭是 No.09 的形状,本章用限流与隔离前向设防。A 很诱人,因为穿透也会把压力送到 DB,但它通常表现为缓慢渗血;这里强调的是故障沿依赖链向上游传播。
到达量 60,800 QPS,配额 15,000 QPS。按公式,实际通过与需要拒绝或降级的量分别约是多少?
A。min(60,800, 15,000)=15,000,超额是 45,800。B 把通过量与超额量颠倒;D 又把到达总量当成拒绝量,忽略了已经成功放行的 15,000。
业务允许短暂突发,希望平均速率受控,最符合哪种算法的特性?
A。令牌可以在空闲期积攒,因此突发开始时能放出高于稳态配额的尖头。B 很诱人,因为它也能保护下游,但漏桶更强调平滑恒速,会把突发改写成排队而不是立即放行。
系统过载,只能保住一组功能。哪组最符合本章的业务层级?
B。这三项直接关系交易、资金与履约,应获得保底配额。D 看似公平,是最诱人的错误项,但随机顺序会让推荐查询与退款争同一份资源;技术公平不能替代业务优先级。