限流别只挡入口:把配额拆到下游依赖上
限流别只挡入口:把配额拆到下游依赖上
入口网关统一限流很常见:超过每秒多少请求就直接 429。上线初期这能挡住突发流量,但线上真正把系统打穿的,往往不是入口 QPS,而是某个下游被放大的调用。一次列表请求可能扇出到库存、价格、推荐三个服务;推荐再去查画像。入口看起来还在配额内,推荐服务已经在排队。
入口配额和依赖配额不是一回事
入口配额回答的是“这台网关还能接多少连接”。依赖配额回答的是“这条下游链路还能承担多少有效工作”。两者的单位经常不一样:网关按请求数计,下游可能按查询复杂度、批大小或连接数计。只在入口设一个数字,等于假设每次请求的成本相同。这个假设在读多写少、且接口形态单一时勉强成立;一旦有批量接口、搜索接口或带重试的客户端,成本会差一个数量级。
一个实用的拆法是给每个下游单独留一个并发上限,而不是只留 QPS。并发上限直接对应线程、连接和在途请求,超了就快速失败,避免把线程池拖死。QPS 适合防刷;并发上限适合保护依赖。两者可以同时存在,但不要互相代替。
按调用放大比拆预算
先量一次“一次用户请求平均打到下游几次”。这个放大比比平均 QPS 更有用。例如入口允许 200 QPS,某接口平均扇出 8 次,下游看到的就是大约 1600 QPS,再叠加客户端超时重试,还会再乘一个系数。预算要按放大后的数字留给下游,而不是按入口数字等比例缩小。
重试尤其容易把限流打穿。上游收到 429 或超时后再试一次,下游的压力是叠加的,不是替代的。限流和重试必须放在一起设计:对幂等读可以有限次重试,对非幂等写默认不重试;重试间隔要带抖动,并且重试次数要算进下游配额,而不是算在配额之外。
失败时先降级再排队
配额用尽时,排队通常比快速失败更差。队列会把延迟从几十毫秒堆到数秒,调用方超时后断开,服务端仍在干活,形成无效工作。更稳妥的顺序是:能降级的读请求返回缓存或空结果并标明降级;不能降级的写请求直接拒绝,让调用方明确失败。只有处理时间很短、到达过程可预期的任务,才值得用很短的队列削峰,并且队列长度要有上限。
降级也要有开关和观测。没有标记的降级会变成“接口成功但数据是旧的”,排障时很难看出来。响应里带一个降级原因,日志里带上游配额名,比事后猜哪一层先满了要快得多。
观测上要看拒绝发生在哪一层
至少分开记四类计数:入口拒绝、下游并发拒绝、下游超时、客户端重试。只看总错误率会把这四类混在一起。入口拒绝升高,说明流量形态变了;下游并发拒绝升高而入口正常,说明放大比或某个热点接口变了;超时升高但拒绝不高,说明依赖变慢,限流阈值可能仍然过松。
把阈值写进配置时,注明它保护的是哪一个资源,以及估算时用的放大比。下次接口新增一个扇出,评审时才知道要不要改配额,而不是等依赖被打满再回头改网关。
限流不是一个全局开关。入口负责挡住明显超量的流量,每个关键依赖负责守住自己的并发和超时。两边的数字分开设、分开看,系统才不会在“网关还是绿的”时候被下游拖垮。
- 点赞
- 收藏
- 关注作者
评论(0)