日期:2026-08-24
一、项目背景 我们网关一开始给每个调用方配的是平配额,大家一个水位线,谁超了谁被限。平时没事,一到大促或者某个业务方做活动,突发流量打满网关,公共资源被一家吃光,其他调用方跟着抖,延迟集体上涨。有一次某部门搞促销,瞬时流量冲到平时五倍,把网关 CPU 占满,连带把另一个核心业务方的实时接口也拖慢,对方投诉说我又没搞活动凭什么跟我限。我们当时只能临时手动调配额,救火一样到处补。那次之后我们认定,网关配额必须分层,还得给突发留弹性,不能所有人都挤一根水管。
弹性额度我们按业务节奏预置,大促前运营在后台点一下把某调用方的突发上限临时抬高,活动结束自动缩回,不用工程师半夜爬起来手动调配额。
分层不是一蹴而就,我们和每个调用方聊了他们的业务曲线,把核心和长尾分开标,刚开始有人嫌麻烦,但第一次大促没再互相踩之后,反对声音就没了,事实比说服有用。
二、落地场景 第一块是调用方分层配额,按业务重要度和签约等级给不同基础配额,核心业务方水位高,长尾业务方水位低但保底。第二块是突发弹性额度,每个调用方在基础配额之上有一块弹性额度,平时不用,突发时自动借用,按时回收。第三块是优先级队列,网关入口按调用方等级排队,高优先级请求在拥塞时先过。第四块是超额熔断与排队,弹性也用尽时,超出部分要么熔断要么进排队,而不是直接拖垮全局。最后是弹性回收,突发过后弹性额度自动释放,避免长期占用。
熔断和排队我们做了分级,轻微超额走排队保请求,严重超额才熔断,避免一有波动就把正常调用全砍了,毕竟网关抖一下下游全跟着抖。
三、关键技术挑战与解决思路 分层最怕分完之后互相踩。我们给每层设了独立配额池,核心层的突发不会吃掉长尾层的保底,长尾层的突发也限制在自有弹性内。突发弹性要有借有还,我们做成时间窗借用,超过窗口自动回收,防止某家活动结束还占着名额。优先级队列要防饿死,低优先级不能永远排不上,我们加了最长等待时间触发提权。熔断和排队的选择靠业务标注,实时性强的走排队保住请求,可重走的走熔断保护网关。
成本侧,分层之后资源利用率上来了,同样网关集群比原来多承载了约两成调用,等于延缓了一次扩容采购,财务那边也乐意。
优先级队列的提权阈值我们调了很久,定太低低优先级永远插队,定太高又失去保护,最后按业务等级设了不同的等待上限,核心业务方允许更长的耐心,长尾则更快被提权,这个差异化是和两个调用方负责人当面聊出来的,不是拍脑袋,落地后两边都没再投诉。
四、效果数据 分层加弹性上线后,那种类一家活动全网关抖的情况没了。一次同类大促再发生时,突发方用弹性额度自己扛住,其他调用方延迟波动控制在百分之十以内,而之前是翻倍。网关整体利用率更平稳,因为弹性把峰值削平了。熔断准确率提升,误杀正常请求的情况基本消失。弹性回收按时生效,没有出现过活动结束后资源被长期占用的现象。
我们还给弹性额度加了可视化,调用方能在控制台看见自己用了多少突发、还剩多少,超了提前知道,不用等被限了才来找我们扯,自助程度高了运维也轻松。
五、可复用经验总结 网关配额平配是最偷懒也最易出事的方案,一家冲峰值全平台陪跑,我们那次核心业务方被无辜限流,投诉比搞活动的还凶。分层加弹性才是正解,每层独立池加自有突发,谁的责任谁扛。优先级队列必须防饿死,不然低优先级永远排不上也是另一种不公平。我后来总结,限流的本质是隔离故障域,不是平均分配,把各管各的设计进去,突发就不怕了。
限流策略要和调用方聊清楚,我们上线前逐个业务方对过配额和突发预期,大家知道自己的水位在哪,活动前提前报备,冲突就少,系统只是把约定落地。
限流这门手艺,调参是细活,但前提是先把分层这件事在流程上定清楚,工具只是把约定落下去。
结语 现在新接入调用方默认进分层模板,近期在做按业务时段的弹性预测,让弹性额度提前预热。
案例片段(已脱敏): 分层配额与弹性片段(伪码):
pool := quota.Pool(app.Tier) // 按等级独立配额池 if r.InRate <= pool.Base { pass(r) } else if r.InRate <= pool.Base + pool.Burst { // 借用弹性 pool.BurstUsed += r.InRate - pool.Base } else { if r.Realtime { queue(r, maxWait) } else {熔断(r) } } // 时间窗到期后 BurstUsed 自动回收复盘那次五倍流量事故,平配下其他调用方 P99 从约 200ms 涨到 420ms;分层弹性后同类事件里,非突发方 P99 波动控制在 220ms 以内,基本无感。