← Back to Blog

限流与熔断不是配置项:把系统保护做成三层机制的完整方法

78 阅读 点赞

去年冬天,我们的订单服务经历了一次典型的”雪崩”。起因很小:下游的库存服务响应变慢,从平均 50ms 涨到了 800ms。订单服务没有做任何限制,请求继续堆积,线程池被占满,接着它依赖它自己的上游——网关、APP 首页、购物车——全部被拖垮。整个链路在四分钟内从”有点慢”变成”完全不可用”。

事后复盘,我们发现问题根本不在那个慢掉的库存服务,而在于订单服务把”慢”当成了”死等”。它没有告诉自己”这个依赖不行了,我该先刹车”。这就是限流与熔断要解决的事——前者控制自己接收多少流量,后者决定什么时候停止向一个不健康的依赖发请求。

很多团队把这两个词当成运维的配置项,出了问题才想起来加。但真正可靠的做法,是把它们当作系统里三层递进的保护机制来设计。

极简科技风格的限流熔断闸门
限流守住入口,熔断切断不健康的依赖——两者共同构成系统的第一道防线

第一层:限流,先守住自己的入口

限流的第一性问题不是”限多少”,而是”限谁”。同一个服务收到的流量,价值并不相同:来自核心交易链路的请求,和来自后台报表导出的请求,对系统的重要性完全不同。如果无差别地按 QPS 掐断,最先被牺牲的往往是真正该保护的那部分。

所以在动手配阈值之前,先给流量分层。通常我们按三个维度切:来源(核心业务、内部调用、第三方)、接口(写接口、读接口、旁路查询)、用户(VIP、普通、爬虫)。分层之后,限流策略才有落点:核心写接口给出保底配额,旁路查询用更严格的阈值甚至直接排队,爬虫类流量走单独的桶。

算法上不必迷信复杂。令牌桶适合允许突发流量的场景,漏桶适合要求恒定输出速率的场景,滑动窗口则能避免固定窗口在边界处的双倍突刺。绝大多数业务用滑动窗口计数加令牌桶限突发就够了,关键不是算法多精妙,而是阈值有依据。这个依据不能拍脑袋,要来自压测和真实流量曲线的分位数——比如 P99 峰值的 1.2 倍作为硬限,80% 作为告警线。

还有一点容易被忽略:限流被触发时,返回给调用方的必须是明确的拒绝信号(比如 429 加上 Retry-After),而不是让它静静超时。一个体面的拒绝,远好过一个让上游苦等三十秒的沉默。

第二层:熔断,在依赖崩掉之前主动断开

限流管的是”进来的流量”,熔断管的是”出去的调用”。如果限流是门口的保安,熔断就是电路里的保险丝——当某个下游持续失败,继续发请求不但救不了它,还会把自己拖下水。

熔断器有三个状态,理解它们就理解了全部。正常时是关闭(Closed),请求正常放行,同时统计失败率;当失败率或慢调用比例超过阈值,进入打开(Open),此时所有请求直接快速失败,不再真正发出,这就是”快速失败”保护自己的关键;打开一段时间后进入半开(Half-Open),放少量探测请求出去,成功则闭合恢复,失败则重新打开。

设计时最重要的是三个参数:统计窗口、触发阈值、半开探测策略。窗口太短会被偶发抖动误伤,太长则反应迟钝;阈值要结合下游的真实容量,而不是拍一个恒定的 50%;半开探测建议用固定数量的请求而非固定时间,避免一恢复就被洪峰再次打垮。

熔断最容易被低估的收益,是它给下游留出了自愈的时间窗口。当所有上游同时停止轰炸,那个过载的服务往往能在几十秒内缓过来——这比任何扩容都来得快。

限流熔断三层保护结构数据可视化
三层保护递进:入口限流决定放不放行,熔断判断发不发请求,降级隔离决定失败影响多大

第三层:降级与隔离,让失败变得可承受

限流和熔断解决的是”别把系统拖死”,但用户最终还是想看点什么。所以第三层是降级:当熔断打开,业务不应该返回大片的 500,而应该返回一个可接受的替代结果。商品推荐服务挂了,就返回热销榜单;评论加载失败,就先展示”评论加载中”;价格服务不可用,宁可展示”稍后刷新”也不要显示错误的价格。

降级的前提是提前设计好兜底逻辑,而不是出事那天临时写 if-else。我们团队的做法是,每个跨服务调用的封装里都强制附带一个 fallback 函数,哪怕它只是返回 null 或缓存值,也必须显式声明”失败时怎么办”。这个约束逼着开发者在写代码时就想清楚降级路径。

隔离则是降级的底座。最经典的是线程池隔离:给不同的下游依赖分配独立的线程池,一个依赖卡死不会耗尽全部线程。配合信号量限流,可以精确控制每个依赖能占用的并发资源。资源隔离的粒度,本质上决定了故障的传播半径——隔离得越细,一次故障的影响面就越小。

三层叠加起来,一个请求的生命周期是这样被保护的:先经过入口限流决定”放不放进来”,再经过熔断判断”要不要发出去”,万一还是失败了,由降级和隔离决定”返回什么、影响多大”。任何一层单独使用都会留下缺口。

深夜运维监控室中的流量曲线与熔断指示灯
保护机制必须可观测:熔断状态、限流拒绝率、降级次数,都应是仪表盘上的常驻指标

结语:保护机制要能被观测,才真的存在

限流、熔断、降级这套东西,最容易犯的错误是”配了就当有了”。但实际上,一个没有监控的熔断器,和没装没有区别——你不知道它什么时候打开、开了多久、误伤了多少正常请求。

所以每一条保护策略,都必须配套可观测性:熔断状态的持续时间、被限流拒绝的请求比例、降级被触发的次数,这些都应该是仪表盘上的常驻指标。更重要的是定期演练——主动注入一次下游故障,看看保护机制是否如预期启动。只有在故障真正来临时验证过的东西,才算真正拥有。

系统不会因为写了代码就变可靠,它只会因为被反复验证而变可靠。限流与熔断,本质上是把”我们相信依赖不会挂”这种侥幸,换成”我们假设依赖随时会挂”的清醒。这份清醒,才是线上稳定性的真正来源。

💬 发表评论