LabHub
学习 学习路径 课程

微服务架构

熔断器的状态机

在 LabHub 中继续学习

一句话总结

电路断路器不会修复故障。当故障确定时,会快速失败,以防止呼叫者的资源耗尽。

概念图: 超时。 · 越往呼叫链的内部越短。 · 隔壁墙(bulkhead)。 · 故障频繁的路径大部分都是这个共享资源。

为什么需要这个?

仅仅重试是不够的。当下游真的死掉的时候,重试只会使情况恶化。而且从呼叫者的角度来看,无论如何都要等待3秒钟的失败呼叫,都是对连接和线程的浪费。

电路断路器如果判断“最近的结果显示,这个服务现在不行”,就会完全不尝试呼叫,立即返回失败。因为失败是按微秒计的,所以呼叫者的资源不会枯竭,下游会得到恢复的呼吸。

怎么行动

三个状态。

电路断路器的三个状态和四个转移——CLOSED全部通过,并将结果记录在窗口中,如果失败率超过临界值,则转为OPEN,立即拒绝,waitDurationInOpenState结束后,转为HALF_OPEN,只允许测试调用,因此结果会回到CLOSED或再次变为OPEN。4xx不会变成失败

CLOSED是正常的。所有呼叫都通过,结果记录在滑动窗口中。窗口内的失败率超过临界值后会变为OPEN。

OPEN是禁止的。所有呼叫都会立即被拒绝。waitDurationInOpenState过去的话会变成HALF_OPEN。

HALF_OPEN是测试。只允许调用一定数量的测试,其结果会返回CLOSED或返回OPEN。

设置值没有正确答案,但有出发点。作者在运营中使用的值是这样的。slidingWindowSize: 10minimumNumberOfCalls: 5failureRateThreshold: 50slowCallDurationThreshold: 3swaitDurationInOpenState: 30spermittedNumberOfCallsInHalfOpenState: 3. 根据服务性质调整临界值——像结算一样,失败是致命的的地方是30–40%,像通知一样可以宽大的地方是60–70%。

这里最重要的设计决定之一是,什么会被视为失败。4xx不是失败。因为是客户发送错误的,所以不是电路介入的问题。有实际事故案例。库存服务开始退还400给特定商品,而这些400被计入失败率,导致电路打开,甚至连正常商品查询都被屏蔽了。电路只需要对5xx、超时、连接失败做出反应。

在现场相遇的样子

第二常见的事故是HALF_OPEN闪烁。permittedNumberOfCallsInHalfOpenState如果设置为1,每次测试呼叫意外失败时,都会再次降到OPEN。在下游只有间歇性响应的情况下,永远无法恢复到CLOSED。写至少3,一般5~10。

第三个。minimumNumberOfCalls有很多实现的默认值为100。在呼叫频率低的服务中,在窗口填满之前,故障就结束了,电路根本就没打开过。“放了电路也没打开”的一半就是这个原因。

而且电路必须和复位一起写。复位是return null这个反馈回路只是将错误转换为 NullPointerException。它必须是缓存值、缩短的响应或明确的指示之一。

光靠电路断路器是不够的。

电路是已经变坏后才反应的装置。前后需要一起放置的东西,四个人聚集在一起才能形成一个防御线。

**超时。是最基本也是最常遗漏的。如果没有超时,电路即使没有机会计数失败,也会无限地锁定呼叫者的线程。值根据对方的延迟分布来确定,但越往呼叫链的内部越短。**如果外面是3秒,里面是5秒,那么在里面回答之前,外面就已经放弃了,里面的工作就会被完全浪费。

**隔壁墙(bulkhead)。**单独限制对一个对象可以使用的同时呼叫次数。如果没有这个,一个速度变慢的服务会占用整个公共线程池或连接池,甚至与该服务无关的功能也会一起停止。故障频繁的路径大部分都是这个共享资源。

**重新尝试。因为是与电路相反的方向运行的,所以一起使用时需要规则。只有对称的呼叫,设定上限,在间隔中随机混合。而且在链条的多个层中各自重新尝试时会乘以。**如果三个层各自重新尝试三次,最坏的情况是27次。重新尝试的原则是只负责链条的一层。

回溯。 当电路打开时,会返回什么。正如前面所说,再次抛出例外值的回溯什么都不是。好的回溯通常是其中之一。稍微旧的缓存值,只删除那个部分的缩小响应,或者以屏幕可以理解的形式告知“现在无法获取此信息”。

最后,这四个人必须考试。平时从未打开过的赛道在真正的障碍时第一次运行,这时如果波尔巴克有缺陷,防御线反而会造成障碍。定期进行故意杀死对手的训练就是这个原因。

下次实习要做的事情

直接实现电路断路器。通过眼睛观察状态转换,通过计数器在OPEN中证明是否真的没有调用到下游,防止4xx被标记为失败,最后将整个转换历史记录到文件中。