云上服务熔断与降级实战:别让一个小故障拖垮整个系统

去年双11,一个电商客户的核心系统崩了。起因是一个非核心服务——商品推荐,响应变慢,从50ms慢慢涨到3秒、5秒,最后超时。推荐服务自己没崩,但调用它的订单服务等不住了。订单服务的线程池被堵满,新请求进不来,开始排队。排队一长,订单服务的上游也开始超时。最后,整个交易链路雪崩,用户下不了单。
一个推荐服务挂了,整站瘫痪。
这是微服务架构最经典的悲剧:一个小故障,通过调用链放大,拖垮整个系统。
今天聊聊云上服务熔断与降级。不是那种“熔断很重要”的废话,而是帮你理清楚:雪崩是怎么发生的?熔断器怎么设?降级策略怎么写?怎么让一个小故障只死它自己,别拉垫背的。
01 雪崩是怎么发生的?
雪崩不是一瞬间发生的,通常经过三个阶段。
第一阶段:超时积累
下游服务响应变慢,上游调用方在等。每个请求多等几秒,线程池里的线程就被占住了,释放不出来。
第二阶段:资源耗尽
线程池满了,新请求进不来,开始排队或直接拒绝。上游服务自己的响应时间也开始变长,影响到更上游。
第三阶段:连锁反应
上游的上游也开始超时、排队、拒绝。一层一层往上传递,直到入口。整个链路瘫痪。
反常识观点:雪崩的根源往往不是服务挂了,而是服务变慢了。 挂了反而好处理,超时积累才是慢性毒药。
02 超时:熔断的前提
很多人设超时,随手填个数:3秒、5秒,觉得够了。但超时设不对,熔断就是摆设。
超时应该怎么设?
先搞清楚下游服务的正常响应时间,P99是多少。
超时时间设为P99的2-3倍。太短,正常请求会被误杀;太长,故障时线程池被占住太久。
不同场景分开设:读接口超时可以短一些,写接口长一些。
一个真实案例:某公司调用推荐服务,正常P99是200ms,超时设了10秒。推荐服务出问题时响应变慢到5秒,还在超时范围内。调用方等5秒,线程池很快被占满,雪崩。后来把超时改成500ms,出问题时快速失败,线程池没满,系统扛住了。
记住:不设超时,等于没有熔断。超时太长,熔断等于没有。
03 熔断器:自动切断故障链路
熔断器有三个状态,像电路开关。
关闭:正常状态,请求正常通过。统计错误率或慢请求比例。
打开:错误率超过阈值,熔断器跳开,后续请求直接失败,不调用下游。
半开:过一段时间,放少量请求过去试探。如果成功,熔断器关闭;如果失败,继续保持打开。
阈值怎么设?
错误率阈值:通常50%。太高,故障影响范围大;太低,容易误熔断。
慢调用阈值:通常P99的2-3倍。
统计窗口:比如过去10秒、20秒、60秒。窗口太小容易抖动,太大反应慢。
休眠窗口:熔断器打开后,多久进入半开状态。通常5-30秒。太短,下游还没恢复就试探;太长,浪费恢复时间。
工具怎么选?
Hystrix(Netflix开源):老牌,功能全,但已进入维护模式。
Resilience4j:轻量级,专为Java 8函数式设计,当前主流。
Sentinel(阿里开源):功能强大,支持动态规则,适合大规模。
云原生服务网格(Istio、Linkerd):在基础设施层做熔断,对应用透明。
04 降级:给用户一个体面的交代
熔断是“停止调用下游”,降级是“熔断之后怎么办”。
降级的核心:别让用户看到白屏或错误码,给一个能接受的结果。
常见降级方案:
返回缓存数据:推荐服务挂了,返回上一次缓存的推荐结果。可能不是最新的,但比没有强。
返回默认值:库存服务挂了,显示“有货”(乐观),或者“暂时无法查询,请稍后再试”。
关闭非核心功能:评论、猜你喜欢关了,核心下单留着。
页面降级:去掉动态模块,显示静态内容。
降级要分等级:
一级降级:只关最不重要的功能。用户几乎无感知。
二级降级:关掉部分次要功能。用户能感觉到少了点东西,但不影响核心操作。
三级降级:只保留核心交易链路。页面变素,功能变少,但还能下单。
那家电商后来改了:推荐服务挂了,订单页面不显示“猜你喜欢”,改成一个静态Banner。用户几乎没注意到变化,订单照下。
05 限流:别让流量冲垮你
熔断和降级是“下游坏了怎么办”,限流是“上游来太多怎么办”。
限流常见算法:
令牌桶:固定速率放令牌,请求来了拿令牌,没令牌就等或拒绝。允许突发流量。
漏桶:请求进桶,以固定速率流出。削峰填谷,适合保护下游。
滑动窗口:统计过去N秒的请求数,超过阈值就拒绝。比固定窗口更精确。
限流阈值怎么设?
压测出单实例的QPS上限,按上限的70-80%设限流阈值,留余量。
不同接口分开设:读接口可以高一些,写接口低一些。
限流后的处理:排队等待、直接拒绝(返回429)、降级到默认值。
反常识观点:限流不是为了不让你超,而是让你超的时候别崩。 超了就拒绝,总比所有请求都超时强。
06 一个真实案例:从雪崩到稳如泰山
回到开头的电商客户。大促复盘后,他们做了几件事:
第一,给所有下游调用加上超时,P99的2-3倍,不设死值。推荐服务从10秒超时改到500ms。
第二,引入熔断器。推荐服务错误率超过50%就熔断,10秒后试探。熔断期间直接走降级。
第三,写降级逻辑。推荐服务挂了,订单页面不显示个性化推荐,改静态Banner。用户几乎没感觉。
第四,入口加限流。API网关按总QPS限流,超过阈值直接返回“系统繁忙,请稍后再试”。
第二次大促,推荐服务又出了问题。但这次,熔断器几秒内就打开了,订单服务直接走降级,整站稳如泰山。运维负责人说:“以前一个小故障能搞死全站,现在它自己死,不拖别人下水。”
写在最后
微服务架构的好处是灵活,坏处是脆弱。一个服务挂了,能拉一串垫背的。
熔断、降级、限流、超时,这几样东西不是“有就行”,要设对。超时太长等于没有,阈值太敏感容易误伤,降级太粗糙用户会骂。
那家电商的运维负责人后来总结了一句话:“以前觉得系统要‘稳’,是不出事。现在觉得,系统要‘韧’,是出了事也能扛住,只死该死的那部分。”
你的系统,够韧吗?