在电气工程中,瞬时故障被定义为断电并恢复供电后消失的错误情况。这也是我们许多人在物理设备性能不佳或卡在充满乱码的蓝色崩溃屏幕上时,无意识地使用的权宜之计:强制关闭再打开电源。
在云计算中,我们面临着日益增加的复杂性、已知未知,或者更糟的是,未知未知,涉及到我们永远不会接触的基础设施以及以指数级速度发展的技术,还有连接不断扩展的数字世界的各种不同的解决方案。如今的虚拟用户对无响应、不可靠和性能低下的产品的容忍度为零——每个人都想要 24x7 正常运行时间以及能够不断发展并融入他们生活方式的解决方案。
在这个新的虚拟世界中,我们不能像以前那样直接走过去重启机器,至少不能在不影响成百上千甚至数百万用户的情况下这样做。而且,在当今竞争激烈的世界中,用户对品牌和产品的忠诚度正在迅速下降;用户很可能会点击一下键盘就去寻找替代服务,并且一去不复返,而不是忍受任何可衡量的停机时间。
让我们快速回顾一下两起令人警醒的事件,它们提醒我们,今天的瞬时故障可能在瞬间发生,难以识别和解决,并且对我们的利益相关者产生深远的影响。
- 艰难时期: “在过去的一周半里,我们的服务遇到了一些问题。对此我感到非常抱歉,并深感歉意。这是自我们的服务重构造成不稳定以来,我们遇到的最大事件,”微软云开发者服务公司副总裁 Brian Harry 在他的博客中写道。经过数周的 sleepless nights,根本原因被确定为对访问控制服务 (ACS) 的请求风暴,这些请求耗尽了源网络地址转换 (SNAT) 端口,阻止了身份验证,并影响了我们的利益相关者。
- 503 错误: “从我们 Azure 功能实现的开始就设置监控,证实了监控在 DevOps 流程中的重要性,”Cellenza 的 Mikael Krief 在 ALM DevOps Rangers 博客上报告说。同样,我们又经历了 sleepless nights,才找到根本原因,弄清楚为什么我们重构的扩展会产生连接和线程风暴,导致我们的 Azure 服务崩溃,并因 503 Service Unavailable 错误而让我们的利益相关者感到沮丧。
我们可以为我们的云应用程序设置故障和灾难恢复,以帮助最大限度地减少(而不是消除)资源故障或自然灾害造成的停机影响。但是,对于使用远程资源或与远程服务通信的解决方案,我们需要对瞬时故障更加敏感。设计良好的解决方案会在发出警报之前(或者更糟的是,变得无响应和失败之前)检测并尝试自我纠正瞬时故障。
有一些瞬时故障处理模式,包括以下白板上显示的三个:重试 (retry)、限流 (throttling) 和 断路器 (circuit breaker)。

重试模式
重试模式是三种瞬时故障处理模式中最简单的一种,也是我们在日常生活中自然而然会做的事情。它可以有效地应用于跨分布式网络通信的解决方案,以处理由网络延迟、服务过载和电源中断等问题引起的瞬时故障。

伪代码
设置 failure_count = 0
调用[微]服务
如果 (失败) failure_count++
如果 (failure_count > retry_limit) 或 (非瞬时故障)失败
延迟(延迟时间)
按 failure_count 的系数增加延迟时间
重试步骤 2
该模式确保用户的请求最终在不太理想的情况下成功,而在这些情况下,瞬时故障原本会导致立即且频繁的失败。有关详细信息,请参阅开源实现,例如 java-design-patterns 和 transient-fault-handling-application-block。
限流模式
我们需要保护我们的服务免受过度使用我们解决方案或由于系统或逻辑故障而失控的客户端的影响。就像一条四车道的隧道为六车道的高速公路提供服务一样,我们必须管理请求(汽车)的流量,并限制超过最大吞吐量(隧道)的端点(车道)。

伪代码
增加 request_count
// 限制 – 一个时间间隔内的最大请求数
// 降级 – 以“减速”错误或暂停操作失败
如果 (request_count > 限制)降级服务
调用[微]服务
该模式帮助我们满足服务级别协议,防止单个用户过度使用系统,优化请求流,并处理突发的意外请求。我们在之前的模式中需要增加重试之间延迟的原因之一是确保我们不会无意中超过系统的吞吐量并触发服务降级。有关详细信息,请参阅开源实现,例如 WebApiThrottle 和 Core.Throttling。
断路器模式
就像您家中的断路器一样,断路器模式是您的最后一道防线。虽然重试模式有助于自动纠正短暂的瞬时故障,但此模式更适合需要较长时间才能解决的瞬时故障。在处理网络或服务中断时,例如 艰难时期 事件,重试失败的服务操作可能会使情况更糟,导致级联故障,并最终引发解决方案崩溃。断路器模式的假设是,只有在经过相当长的延迟后自动重试失败的服务调用,才有可能成功。
就像您在黑暗中蹒跚进入地下室寻找断路器箱一样,您是在允许电气系统和潜在的静电荷恢复,然后再拨动开关。

伪代码
// 断路器未跳闸
如果 (circuit_state == open)
调用[微]服务
如果 (失败) fail_count++
如果 (fail_count > 限制) circuit_state = closed
// 断路器已跳闸
否则
如果 (circuit_state == closed) 启动定时器
// 回调定时器事件
定时器超时时
调用[微]服务
如果 (成功) circuit_state ==open
有关详细信息,请参阅开源实现,例如 Hystrix、circuit-breaker 和 Polly。
不要害怕故障
请记住,为所有已知故障和已实现的处理模式包含单元测试和集成测试。您的单元测试必须验证您的解决方案在触发故障处理逻辑时能做出适当的反应。另一方面,您的集成测试必须模拟弹性故障,以验证您的集体服务解决方案可以有效地处理故障。您可以使用服务虚拟化(例如 Hoverfly)来模拟服务、瞬时故障和降级服务。如果您的解决方案和相关的故障处理模式未能兑现自我修复和避免灾难性崩溃的承诺,您的利益相关者将不会感到高兴。
因此,故障和失败一样,是 无责 DevOps 的一个特性,我们应该 不要害怕它们。为了保持竞争力,我们必须提高我们的基础设施、解决方案和责任制的质量标准,以便在根本原因级别检测、修复和自我纠正,从而维持可接受的服务水平。
例如,在下面的图中,微服务 #7 已经崩溃,触发了断路器和流量限制,并允许系统恢复,同时继续为用户提供服务。从这个简单的示例中可以看出,故障的组合以及处理故障的难度可能会在功能标志的开关之间变得复杂。

这些和其他模式是 健康的 DevOps 思维模式的核心价值观 之一的强大盟友,即“超越当今流程的限制进行改进——努力始终在可重复的流程和框架之外进行创新和改进。” 它们帮助我们提高质量标准,并持续交付业务价值,让我们的利益相关者满意。
特别感谢 Brent Reed 的坦诚审查和反馈,这有助于我们改进和分享我们的见解。
2 条评论