限流降级治理:统一规则中心与执行链路 — editorial cover photo

限流各服务各搞一套,出事时一团乱

各服务各搞各的限流降级,出事时一团乱——治理的核心是统一规则中心。

2025年10月1日·返回文章列表
文章大纲

Matrix 系统做限流改造前,限流逻辑散落在各个服务里。网关用 Nginx 自带的连接数限制,人群服务用 Guava 的 RateLimiter,策略服务手写了一套滑动窗口,还有几个团队干脆用配置开关加 if 判断硬扛。大促压测时出了个尴尬的事,上游策略服务的 QPS 才到 1300 就开始拒绝请求,排查发现是人群服务的 Guava 限流配额配低了,但没人知道,因为规则散在各处,没有一个地方能一眼看清当前哪个接口在限流、阈值是多少、拒绝率有多高。

那次之后我们下决心做统一治理,把限流降级规则收敛到一个规则中心,配合控制台做全局可视化和动态下发。改完之后上游 QPS 稳定扛到 2800,大促全程零资损。这组结果让我相信,限流降级治理的核心矛盾不是技术选型,而是统一性和全局视角。

散落的真正代价

限流降级散落在各服务的危害,平时看不出来,出事时才暴露。每个团队按自己的理解实现一套,技术栈不统一,Guava、Sentinel、自研混着用。阈值各自配置,没有全局视角,A 服务把限流配严了,B 服务还以为有富余,实际链路早被 A 卡死。降级开关散落在配置文件、数据库、硬编码里各处,应急时根本不知道有哪些开关、在哪、怎么触发。

更深的问题是行为不一致。同一个服务的多个实例,配置如果靠本地文件,改了之后实例间不同步,部分实例限流生效部分没生效,流量分配混乱。手写的限流逻辑往往没有降级配套,超限的请求直接抛异常,用户看到的全是报错。

这些问题的共同根源是没有统一的规则源。治理的第一步,就是把规则收口。

统一规则中心要解决的三件事

规则中心不是一个新框架,它要解决的是配置、下发、可视三件事。

配置集中。所有限流降级规则在一个地方定义,包括限流维度、阈值、策略、降级方式,定义完存到配置中心(比如 Apollo、Nacos)或专门的规则存储。规则的格式统一,不再各服务各写各的。

下发实时。规则变更后秒级推送到所有服务实例,多实例同步生效,不用重启。这一步是动态限流的基础,大促时根据大盘流量实时调阈值,靠的就是实时下发。

限流必须配套降级方案

光限流不配降级,是限流治理最常见的反模式。被限的请求返回什么,必须在定义规则时就写清楚,返回兜底数据、排队等待、还是友好提示。用户看到的如果是裸异常,体验比不限流还差。限流是手段,降级保体验才是目的。每条限流规则都要回答一个问题,被拒的请求怎么办。

可视全局。控制台展示当前所有规则的执行状态,哪个接口在限流、QPS 多少、阈值多少、拒绝率多高,哪些功能已降级、哪些依赖被熔断。出事时值班人员一眼看清全局,而不是挨个服务翻日志。这一步经常被忽视,但它的价值在应急时刻最大。Matrix 改造前最痛的就是出事时不知道谁限了谁降了,可视化补上之后排查时间大幅缩短。

Sentinel 的控制台是这套思路的现成实现,规则在控制台配置,下发到各服务的 Sentinel 客户端,执行状态上报回控制台。如果不想直接用 Sentinel,自研规则中心也遵循同样的三件事。

PlantUML
正在加载图表…
PlantUML diagram

图:统一规则中心三件事——配置集中、下发实时、可视全局,出事时一眼看清谁限了谁降了

规则要分层,各层保护不同对象

限流降级规则不能一股脑全堆在网关,要分层,每一层保护不同的对象。

网关层做全局限流,保护整个系统不被超出容量的流量冲垮,超过总量的请求在入口就拒绝。这是第一道闸门。

服务层保护单个服务。某个服务处理能力有限,超过它的阈值就限流,避免这个服务被打垮后拖累调用方。

接口层保护关键接口。下单、支付这类核心接口单独配限流规则,保证即使其他接口出问题,核心交易链路不受影响。

资源层保护底层资源。DB 连接数、Redis 连接数、线程池大小,这些都是共享资源,某个逻辑把它们占满了,其他逻辑就动不了。资源层限流保证关键资源不被个别操作独占。

这四层不是孤立的,而是层层兜底。网关挡住的流量最多,但粒度最粗。越往下粒度越细,保护的对象越具体。多层配合才能既保全局又保关键路径。

PlantUML
正在加载图表…
PlantUML diagram

图:限流四层保护——网关层挡总量、服务层保单体、接口层护核心交易、资源层防独占

限流维度和策略的选择

每条限流规则要明确几个维度。按什么限,QPS 还是并发数。QPS 限流关注单位时间请求量,适合大多数接口。并发数限流关注同时处理的请求数,适合处理时间较长、想防止线程占满的接口。Matrix 用 Lua 脚本实现分布式限流,在 Redis 上原子地做令牌桶计数,多实例共享同一个限流配额,比各实例本地限流精确得多。

按调用方区分也是常见需求。同样一个接口,内部调用的配额和外部调用的配额分开,保证核心链路不被低优先级调用挤占。这叫来源限流或调用方限流。

超限后怎么办,是策略要回答的。直接拒绝最简单,但用户体验差。排队等待让请求匀速通过,适合可接受排队的场景。匀速通过(令牌桶)控制整体速率,允许一定突发。选哪种取决于业务对延迟和拒绝的容忍度。

阈值不能拍脑袋,要基于压测。压测出系统的容量曲线,阈值设在容量的安全水位(比如压测极限的 70%)。更进一步,阈值设多档位,正常、警戒、极限,大促时动态切换。Matrix 大促期间会把限流阈值临时调到极限档位压榨容量,平时回到正常档位留安全余量。

降级要分级,按压力逐级启用

降级不是一刀切关掉,而是分级别,压力越大降得越深。

L1 读降级,读失败时返回缓存或默认值,用户基本无感。这是成本最低的降级,适合推荐、榜单这类非强一致数据。

L2 功能降级,关闭非核心功能,比如评论、推荐、统计上报。主交易链路不受影响,但用户能感知到部分功能缺失。

L3 写降级,写操作转异步或排队。这是有损降级,数据最终一致,但实时性下降。

L4 拒绝服务,只保最核心的交易,其他全部拒绝。这是最后手段,通常只在系统濒临崩溃时启用,保住命比保体验重要。

分级的意义在于精细控制。不是一有问题就跳到 L4,而是根据压力逐级加深,尽量在低级别解决问题,把对用户的影响降到最小。值班人员根据大盘压力选择启用哪级,这比一个全局开关灵活太多。

PlantUML
正在加载图表…
PlantUML diagram

图:降级四级 L1→L4——读降级、功能降级、写降级、拒绝服务,按压力逐级启用

规则的动态调整和版本管理

限流降级规则不是配一次就固定,它要随流量变化动态调整。大促前调严保护系统,大促后调松恢复体验。规则中心支持实时下发后,这种调整不需要重启,值班人员根据监控大盘在控制台切换档位即可。

规则变更要留版本和审计。谁在什么时候改了什么规则,改之前的值是多少,这些都要记录。限流阈值调错是真实存在的事故源,调低了正常流量被限,调高了系统被打垮。有了审计,出事时能追溯是哪次变更引起的,也能快速回滚到上一个已知正常的版本。

高风险变更(比如大幅调低限流阈值)要走灰度,先在部分实例或部分流量上生效,观察没问题再全量。这套机制和代码发布的灰度逻辑一样,规则也是一种变更,同样需要敬畏。

Continue Reading

关联文档推荐

查看全部
熔断降级:Hystrix 与 Sentinel 的容错哲学 — editorial cover photo限流算法全景:从计数器到令牌桶的工程实现 — editorial cover photo降级开关治理:统一管控与一键熔断 — editorial cover photo
作者:archy.shawn
声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。