分布式系统中亚稳定失败状态
这篇文章的初衷,是记录拜读由Nathan Bronson, Aleksey Charapko, Abutalib Aghayev, and Timothy Zhu共同发表的论文Metastable Failures in Distributed Systems的收获,这篇论文描述了一个在大规模分布式系统中很常见的失败场景:亚稳定失败(metastable failures),它们为什么通常在高负载分布式系统中发生,以及解决问题的思路框架:如何识别和从亚稳定失败中恢复,甚至如何避免发生亚稳定失败。
现实世界中的亚稳定失败
下图是某个公园中非常著名的徒步路线中非常关键的一部分:两座山之间一段狭长的山脊,在两座山之间徒步,只有扶着铁链穿过这段山梁才能保证安全。可以假想这段铁链就是一个分布式系统。
文章图片
当公园不对徒步者人数做限制时,就有可能引起人群拥挤以至于长时间在起点等待去尝试减低负载。你可以想象一下”系统“经历了以下的状态转换。
- 稳定状态(Stable state)
当人群低于某个安全阈值时,任何一个风险因素(例如:缓慢通过铁链的徒步者)都可能引起降速,但是系统仍然能够自愈。 - 脆弱状态(Vulnerable state)
拥挤人群的数量持续增加超过了某个阈值,对于每段铁链的竞争也会增加 —— 下山的人必须要等待上山的人通过,或者干脆冒险不使用铁链而绕过他们。同样,上山的人也必须等待下山的人通过。当这种模式发生时,只要拥挤人群的数量低于某个阈值,系统仍然是可以工作的,但它现在是非常脆弱的,可能变成不可自愈的失败状态。 - 亚稳定失败状态(Metastable Failure State)
当系统处于脆弱状态,某些风险因素可能导致情况恶化。想象一下,一组徒步者需要花费更长的时间通过铁链,这将会使其他上山和下山的人速度降下来。越来越多的人等待,导致在铁链两端和身在其中的剩余空间越来越狭窄,进而导致人们需要更多的时间通过,进一步导致更多的人等待...情况循环恶化 —— 进入亚稳定失败状态。
文章图片
保持系统处于亚稳定失败状态的正反馈循环
亚稳定失败定义 关于亚稳定失败的定义论文中描述如下
亚稳定失败发生在对于负载来源没有控制的开放系统中,一个风险因素导致系统进入一个糟糕的状态,并且会持续存在甚至风险因素被移除。在这个状态系统的效率通常会很低,并且会产生持续影响——通常使工作负载放大或者整体效率降低——阻止系统从这个糟糕的状态中恢复。
Metastable failures occur in open systems with an uncontrolled source of load where a trigger causes the system to enter a bad state that persists even when the trigger is removed. In this state the goodput (i.e., throughput of useful work) is unusably low, and there is a sustaining effect — often involving work amplification or decreased overall efficiency — that prevents the system from leaving the bad state.
文章图片
如果这样,那么为什么不总是运行在稳定状态?诚如,在许多系统中高效的资源利用是非常重要的,所以许多系统选择运行在脆弱状态而不是稳定状态,已获得更高的效率。
当系统处于亚稳定失败状态,系统不会自愈,想要恢复需要采取重要措施,例如降低负载。
问题的症结:正反馈循环 正反馈循环是亚稳定失败发生的根源。例如,在维基百科中给出了这样例子:
A生产了更多的B,B反过来又生产了更多的A又例如,人群狂奔:假设美国纽约街道上正在进行独立日游行,突然有人开始奔跑(A)导致周围人群开始恐慌(B),更多的人开始奔跑(A)更多的人开始恐慌(B)更多人开始奔跑。。。最终导致人群狂奔(也许现在还没有枪响)。
可能有很多不同风险因素能够将系统带到亚稳定失败状态。但是,能够将系统保持在这种状态甚至在移除该风险因素之后仍然不能发生变化的,是自我维持的反馈循环。
亚稳定失败的例子
- 重试导致工作负载放大
假设我们有一个web服务端承接客户端请求,持久层数据库服务的性能为300 QPS和100毫秒的延迟,服务端有一个原生的重试机制:每次请求失败都会立即重试。
然而,当负载超过150QPS,系统将进入“脆弱”状态——假设负载为280QPS,当处于这种状态时系统依然还是可以工作的。现在,如果在服务端和数据库直接有10S的网络断开,数据库将面对流量的陡增(原始流量+重试),这将增加请求延迟进而导致更多的重试,进一步增加请求延。导致一个自续的循环。
- 为happy path所做的优化工作
任何为happy path优化而做出的努力都更可能进入亚稳定失败状态。假设上边同样的系统但是有一个缓冲服务以降低数据库服务的调用次数。如果缓存命中率是90%,那么服务端在脆弱状态下能够处理3000QPS(服务端只有300QPS)的负载。但是如果缓存服务由于某些原因需要重启,那么我们将面临一个问题:它将是完美进入到亚稳定失败状态的诱因。论文中将3000QPS称为表面容量(advertised capacity)(系统将处于脆弱状态的上限),而300QPS是隐含容量(hidden capacity)(系统能够自愈的上限)。
如何处理已知的亚稳定失败 在论文中描述了一些预防已知亚稳定失败的技术:
- 专注于风险因素的持续反馈循环
去定位一个亚稳定失败模式,专注于弱化正反馈循环,这涉及到工作负载放大,而不是针对风险因素打地鼠。 - 识别最强反馈循环
理解哪里是产生工作负载放大最大的实例,然后为它定义一个最大值。大体上,识别最强反馈循环并且不要追逐每一个反馈循环。
如何识别他们呢?压力测试是一个方式。但是挑战是越大规模的服务,反馈循环越强,所以第一次在大规模服务中会发现很多问题。
因此,你能做的事情是识别被风险因素影响的特征指标:例如,上述实例中的重试率。通过这样,你可以理解稳定、脆弱和亚稳定失败状态的边界。 - 弱化反馈循环
论文描述了一些弱化反馈循环的方法。例如包括削弱负载和熔断机制。主要的挑战是这将由客户端来决策何时重试和故障转移,但是客户端通常没有全局信息(并且试图去提供这种服务本身也可能导致失败)。区分短暂的负载峰值和持续性的过载是非常重要的,你可能需要有指标帮助你去准确的识别这两者。
总结 希望通过以上的讨论能有所收益,希望下次在发现”一些列的请求失败“、”死亡螺旋“、”彻底失败“等等,你可以使用这些作为一个思考框架或者通用语言去理解他。论文中还有许多其他的示例和思考,感兴趣可以阅读一下论文原文。
【分布式系统中亚稳定失败状态】原文地址:Metastable Failures in Distributed Systems: What Causes Them and 3 Things You can do to Tame Them
推荐阅读
- 写出个灵活的系统竟然可以如此简单!小白也能写出高级的Java业务!
- go-zero微服务实战系列(十、分布式事务如何实现)
- 剑指微服务分布式|Java旅游管理系统的设计与实现毕业设计
- SpringBoot自定义starter开发分布式任务调度实践
- 阿里云有奖体验(如何通过ECS挂载NAS文件系统)
- 知识管理系统有什么作用(能为企业带来哪些好处?)
- 企业微信SCRM系统帮助企业提高团队效率
- 一个开发者自述(我是如何设计针对冷热读写场景的 RocketMQ 存储系统)
- 虚拟机专用Win10系统/win11系统「支持M1」
- 基于Netty,徒手撸IM(一)(IM系统设计篇)