微服务架构下 熔断 的设计与权衡
前段时间线上出了个事,订单服务依赖的优惠券接口突然变慢,响应时间从几十毫秒飙到十几秒,订单服务的线程池很快被这些挂着的请求占满,整个下单链路跟着一起瘫。最气人的是,优惠券服务后来自己缓过来了,订单服务却还没醒,重启了两台才救回来。事故过后我就想着把熔断这玩意儿认真补一补,查了半天资料,讲原理的文章一抓一大把,讲参数到底怎么定的却少得可怜,可能是我瞎没搜到吧...这篇就把自己的理解加一个正在线上跑着的配置记录一下。
熔断到底在防什么
说白了,熔断防的不是下游直接挂,而是下游“慢”。
下游直接挂掉反而是好事,连接拒绝、503,请求立刻失败,线程立刻释放。真正要命的是那种半死不活的状态:能建连,也能吐响应,就是特别慢。每个请求都挂在那儿等超时,线程池被一点点吃光,然后上游所有接口全被拖下水。这就是所谓的雪崩,听着挺玄乎,本质就是“慢”这个东西会顺着调用链往上传染。
熔断器干的事,就是在发现下游持续不正常之后,直接把请求挡在门口快速失败,别再往下游送了。不然你就眼睁睁看着线程池被慢慢吃光吧。
三个状态,转来转去
经典的熔断器就三个状态:Closed、Open、Half-Open。
Closed 的时候正常放行,后台默默统计失败率和慢调用比例;一旦超过阈值就切到 Open,所有请求直接拒绝,拒绝这个动作本身很快,所以线程不会被占住;Open 状态维持一个冷却时间过后,切到 Half-Open,放一小撮请求进去探路,成功率高就回到 Closed,不行就继续 Open。
Half-Open 这个设计我觉得挺妙,它等于承认了“我根本没法知道下游到底恢复没有”,所以只能用很小的代价去试探。下游发版重启的那阵子,基本就是靠这个状态扛过来的。
一份实测能用的配置
我们这边是 Spring Boot + Resilience4j,Feign 调用上直接套的 CircuitBreaker,配置大概长这样:
resilience4j:
circuitbreaker:
instances:
couponService:
slidingWindowSize: 20
minimumNumberOfCalls: 10
failureRateThreshold: 50
slowCallDurationThreshold: 2s
slowCallRateThreshold: 80
waitDurationInOpenState: 15s
permittedNumberOfCallsInHalfOpenState: 3
意思是:最近 20 次调用里失败超过一半,或者 80% 的请求慢于 2 秒,就熔断,Open 15 秒过后放 3 个请求试探。如果你们用的是 Sentinel,配置项名字不太一样,但思想是一个意思,照着文档对号入座就行。配完重启服务,不出问题的话,在 actuator 的 metrics 里就能看到熔断器的状态了。
权衡:阈值永远没有标准答案
这地方我踩了不少坑,挑几个印象深的说说。
首先是超时和慢调用阈值必须对齐。刚开始我把 HTTP 超时设成 30 秒,slowCallDurationThreshold 设成 2 秒,结果熔断还没来得及开,线程池先满了,因为线程还是得挂 30 秒才释放。后来把超时压到 3 秒,整套东西才自洽。原则上熔断的反应速度要快于线程池被拖满的速度,不然配置写得再漂亮也是摆设。
其次,阈值太灵比太钝更麻烦。失败率 50% 看着挺合理,但下游偶尔抖一下,或者某个本来就低成功率的接口跑进来凑数,熔断器就来回开关,日志里全是状态翻转,比不熔断还吵。后来我把滑动窗口加大、minimumNumberOfCalls 抬高,才算稳下来。
最后,熔断本身是有代价的。你为了保住订单服务把优惠券接口掐了,那用户就享受不到优惠,下单必然受影响。熔断从来不是让系统“没事”,而是把“全挂”换成“部分挂”。这笔账划不划算得看业务,不是纯技术问题。
熔断之后别忘了兜底
请求被拒绝之后总得有个去处。常见的就几种:读缓存、给默认值、排队稍后重试,或者干脆明着告诉用户“活动太火爆,请稍后再试”。
有个坑值得单独拎出来:fallback 里千万别再调同一个下游。我就干过在降级方法里去查备用优惠券服务的事,结果备用服务和主服务共用一个数据库,主库一抖两个全挂,熔断等于白做。兜底逻辑一定要独立,哪怕退化成“这次不用优惠券也能下单”,也比层层依赖靠谱。
另外熔断事件最好打点上报,什么时候开的、什么时候半开的,都记下来。不然出了事,你连下游当时抖没抖过都不知道,排查全靠猜。
写在最后
熔断器本身没什么复杂的,几个状态几行配置,一上午就能跑通。难的是那些参数,阈值定多少、窗口开多大、兜底怎么给,没有标准答案,只能结合自己的流量和业务慢慢试。我上面那套配置也只在我们这个量级勉强够用,没准儿你拿过去就水土不服,建议先在预发压一压再说。
这篇就当给自己留个笔记,下次再出事,起码知道该先看哪儿。
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。