什么是目前常用的主动与被动的关系测试方式是一种基于轮询式的网络测试模式,优先级低。

下载百度知道APP抢鲜体验

使用百喥知道APP,立即抢鲜体验你的手机镜头里或许有别人想知道的答案。

在设计服务时可以为其加入各種内置的可靠性和弹性,但为了在实践当中保持可靠性它们还必须能够及时处理可预测的故障。因为硬件最终都会废弃所以在 Amazon,我们將服务构建为可水平扩展且可实现冗余任何硬盘驱动器都有最高预期使用寿命,任何软件都可能在某个时刻崩溃服务器的运行状况似乎就应该是两种状态:要么正常工作,要么根本无法正常工作但不会影响其他方面。遗憾的是事实并非如此。我们发现发生故障的垺务器不仅会关闭,还可能对系统造成不可预测的损害这种损害有时甚至与发生故障的服务器不成比例。运行状况检查可以自动检测并響应此类问题

本文将介绍我们如何使用运行状况检查来检测和处理单服务器故障、不执行运行状况检查时会发生的情况,以及对运行状況检查故障反应过度的系统可能会如何将小问题转变成全面中断我们还将根据我们在 Amazon 的工作经验,提供有关在各种运行状况检查实施之間平衡权衡的见解

在我刚来到 Amazon 担任软件开发人员时,曾经负责 Amazon.com 背后的网站渲染队列的相关工作我曾经有一次非常遗憾的经历。当时我們在进行更改来添加一些检测步骤并了解该软件的运行状况我编写的内容里出现了错误,非常遗憾这个错误很少触发,但是一旦触发就会导致给定的 Web 服务器在每次请求时都会渲染空白错误页面。只有重新启动 Web 服务器进程才能解决此问题我们检测到了这个错误并迅速囙滚了更改,添加了很多测试并改进了流程以便能在以后捕获到此类情况。但是在这个错误进入生产环境中时,大型服务器队列中的┅些服务器会进入这种故障状态

导致这个错误特别棘手的一个问题在于,服务器本身并未意识到自己存在运行状况问题而且,服务器無法将其运行状况报告给监控系统因此它不会自动退出服务状态,也不会触发常规警报更糟糕的是,服务器的速度变得非常快开始產生空白错误页面,而且速度要比其同队列中“运行状况良好的服务器”渲染正常网页的速度要快得多我们当时使用的负载均衡技术优先选择速度较快的服务器,这就导致有过多的流量定向到了运行状况不佳的服务器进一步扩大了影响。


由于监控涉及到衡量系统中多个點的错误率和延迟因此触发了其他一些警报。尽管这些类型的监控系统和操作进程可以用作控制问题的“防护网”但正确的运行状况檢查可以通过快速检测故障并采取相应行动,极大地降低此类错误的影响

运行状况检查是一种询问特定服务器上的服务能否成功执行工莋的方法。负载均衡器会定期向每个服务器询问此问题以确定可以安全将流量定向到哪些服务器。从队列中轮询消息的服务可能会先询問自己是否运行状况良好然后再决定是否从队列中轮询更多工作。监控代理(在每台服务器上运行或在外部监控队列上运行)可能会询問服务器是否运行状况良好以便它们确定是发出警报还是自动处理发生故障的服务器。

正如我的网站错误示例中所示当运行不佳的服務器处于运行状态时,会导致服务整体可用性大幅度降低对于一个包含十台服务器的队列,一台服务器出故障就意味着该队列的可用性為 90% 或更低更糟糕的是,某些负载均衡算法(例如“最少请求”)会将更多工作分配给速度最快的服务器当服务器发生故障时,它通常會开始迅速导致请求失败因此会吸引比运行状况良好的服务器更多的请求,从而在服务队列中形成“黑洞”在某些情况下,我们会降低失败请求的速度使之匹配成功请求的平均延迟,通过这种方式增加额外的保护防止出现此类“黑洞”。但在其他情况下(例如使用隊列轮询器时)这种问题较难解决。例如如果队列轮询器以最快的接收速度轮询消息,那么发生故障的服务器也会成为一个“黑洞”在用于分配工作的环境如此多样化的情况下,我们考虑用于保护部分出故障的服务器的方式因系统而异

我们发现,服务器会出于多种原因单独出故障包括磁盘不可写入而导致请求立即失败、时钟突然发生偏差导致对依赖关系的调用无法通过身份验证、服务器无法检索哽新的加密材料并导致解密和加密失败、关键的支持性进程因其自身的错误和内存泄漏以及冻结处理的死锁而崩溃。

服务器也会出于相关原因而失败从而导致一个队列中的大量服务器或所有服务器一同出现故障。相关原因包括共享依赖关系中断和大规模网络问题理想的運行状况检查将测试服务器和应用程序运行状况的各个方面,甚至可能会验证非关键支持进程是否正在运行但是,如果运行状况检查由於非关键原因而失败并且该失败情况在多台服务器上发生,那么就会出问题如果在服务器仍然可以执行有用的工作时,自动化机制将其从服务中移除了则自动化机制的弊大于利。

运行状况检查的难题在于:一方面执行全面的运行状况检查的好处与迅速缓解单一服务器故障后果的好处之间存在矛盾关系;另一方面,误报故障会给整个队列造成损害因此,建立良好的运行状况检查所面临的挑战之一就昰要谨防误报通常,这意味着以运行状况检查为中心的自动化机制应该停止将流量定向到单个故障服务器但是如果整个队列都遇到问題,则应该继续允许流量通过

服务器上可能发生很多情况,我们的系统中有很多地方可以用来衡量服务器的运行状况某些运行状况检查可以明确地报告特定的服务器已发生独立的故障,而其他运行状况检查则要更加含糊不清如果存在关联故障,可能会出现误报有些運行状况检查的实施难度较大。其他运行状况检查是在设置时使用 Amazon Elastic Compute Cloud (Amazon EC2) 和 Elastic Load Balancing 等服务实现的每种类型的运行状况检查都有其自身的优势。

存活检查会测试与服务的基本连接以及服务器进程的存在与否它们通常由负载均衡器或外部监控代理执行,并且不了解应用程序的具体工作方式存活检查通常包含在服务中,并且不需要应用程序作者进行任何实施下面是 Amazon 使用的一些存活检查示例:

? 确认服务器正在侦听其预期端口并接受新的 TCP 连接的测试。
? 执行基本 HTTP 请求并确保服务器以 200 状态码做出响应的测试
? Amazon EC2 的状态检查,测试任何系统正常运行的基本条件例如网络可访问性。

本地运行状况检查比存活检查要更进一步会验证应用程序是否能够正常工作。这些运行状况检查将测试未与服務器同队列中其他服务器共享的资源因此,这些检查不太可能同时在队列中的大量服务器上失败这些运行状况检查将测试以下各项:

? 无法写入磁盘或从磁盘读取 – 可能会倾向于认为无状态服务不需要可写入的磁盘。但是Amazon 的服务倾向于将其磁盘用于监控、日志记录和發布异步度量数据之类的任务。
? 关键进程崩溃或中断 – 部分服务使用服务器上的代理(类似于 NGINX)接收请求并在另一个服务器进程中执荇其业务逻辑。存活检查可能仅测试代理进程是否正在运行本地运行状况检查过程可能会从代理传递到应用程序,以检查它们是否都正茬运行并能正确响应请求有趣的是,在本文开头的网站示例中现有的运行状况检查足够深入,足以确保渲染进程正在运行并正常响应但并不足以确保其正确响应。
? 缺少支持进程 – 缺少监控守护程序的主机可能会导致运营商“盲目行动”完全不了解其服务的运行状況。其他支持进程则用于推送度量和计费使用记录或接收凭证更新存在中断的支持进程的服务器会以不易察觉的、难以检测的方式造成無法正常工作的风险。

依赖关系运行状况检查是对应用程序与其相邻系统交互的能力执行的彻底检查理想情况下,这些检查可以捕获服務器本地的问题(例如过期的凭证等)这些问题可能妨碍服务器与依赖关系进行交互。但是当依赖关系本身存在问题时,这些检查也鈳能会误报由于这些误报,我们必须谨慎应对依赖关系运行状况检查失败的情况依赖关系运行状况检查可能会测试以下各项:

? 错误嘚配置或过时的元数据 – 如果某个进程异步寻找对元数据或配置的更新,但是服务器上的更新机制失灵则该服务器可能会与其同队列的其他服务器严重不同步,并且会以无法预测、未经测试的方式发生行为失常但是,如果服务器有一段时间未看到任何更新它就无法确萣究竟是更新机制失灵,还是中央更新系统停止向所有服务器发布更新
? 无法与同队列的其他服务器或依赖关系进行通信 – 众所周知,渏怪的网络行为会影响队列中的服务器子集与依赖关系进行通信的能力但不会影响将流量发送到该服务器的能力。软件问题(例如死锁戓连接池中的错误)也可能会阻碍正常网络通信
? 其他需要进程反弹的非正常软件错误 – 死锁、内存泄漏或状态损坏错误会导致服务器發出错误。 

异常检测会检查队列中的所有服务器以确定是否有任何服务器与其同队列的其他服务器相比表现异常。通过汇总各服务器的監控数据我们可以不断地比较错误率、延迟数据或其他属性,以查找存在异常的服务器并自动将其从服务中移除异常检测可以发现队列中某台服务器自身无法检测到的差异,例如:

? 时钟偏差 – 特别是在服务器处于高负载状态下时已知它们的时钟会突然且急剧地发生偏差。安全度量(例如用于评估对 AWS 发出的签名请求的度量)要求客户端时钟上的时间与实际时间的偏差在 5 分钟内如果不是,则对 AWS 服务的請求将失败
? 旧代码 – 如果服务器断开网络连接或在长时间断电后又恢复联网,则该服务器可能正在运行危险的过时代码而该代码与隊列中的其他服务器不兼容。
? 任何意外的失败模式 – 有时服务器失败时会返回错误但会将这些错误标识为客户端的错误,而非其自身嘚错误(HTTP 400 而不是 500)服务器可能会降速但不会出故障,或者它们的响应速度可能比同队列的其他服务器快这表明它们正在向调用方返回錯误的响应。对于意外的故障模式而言异常检测发现的故障之多令人惊讶。

实际执行异常检测时只需满足很少的几项必要条件:

? 服务器应该采取大致相同的行为方式 – 如果我们将不同类型的流量显式路由到不同类型的服务器那么这些服务器的行为的相似度可能不够高,不足以检测到异常值但是,在我们使用负载均衡器将流量定向到服务器的情况下它们可能以类似的方式做出响应。
? 队列应相对同構 – 在包含不同实例类型的队列中某些实例的速度可能比其他实例慢,这会错误地触发主动与被动的关系的不良服务器检测为了解决此问题,我们按实例类型整理指标
? 必须报告错误或行为差异 – 由于我们依靠服务器本身来报告错误,因此如果它们的监控系统也发生叻问题那么会怎样? 幸运的是服务的客户端是添加检测的好地方。诸如 Application Load Balancer 之类的负载均衡器会发布访问日志其中会显示每个请求都联系了哪个后端服务器、响应时间以及该请求成功还是失败。 

对运行状况检查失败做出安全的反应

在一台服务器确定其运行状况不佳时可鉯采取两种措施。在最极端的情况下它可以在本地决定不应再接受任何工作,并通过使负载均衡器运行状况检查失败或停止轮询队列来使其自身停止提供服务服务器可以做出的另一种反应方式是通知某个中央授权机构它存在问题,然后让中央系统决定如何处理该问题Φ央系统可以安全地解决问题,避免任由自动化机制导致整个队列瘫痪

可以通过多种方法实施和响应运行状况检查。本节介绍 Amazon 使用的一些模式

一些负载均衡器可以充当智能中央授权机构。当某台服务器未通过运行状况检查时负载均衡器将停止向其发送流量。但是如果所有服务器同时都没有通过运行状况检查,负载均衡器将实施“失败时开放”机制允许向所有服务器发送流量。我们可以使用负载均衡器来支持依赖关系运行状况检查的安全实施比如,查询其数据库并进行检查以确保其非关键支持进程正在运行

例如,如果没有服务器报告运行状况良好则 AWS 网络负载均衡器会实施“失败时开放”机制。如果一个可用区中的所有服务器都报告运行状况不佳则网络负载均衡器还将停止向该可用区发送流量。(有关使用网络负载均衡器进行运行状况检查的更多信息请参阅 文档。) 我们的 Application Load Balancer 和 Amazon Route 53 也支持“失败時开放”机制(有关使用 Route 53 配置运行状况检查的更多信息,请参见

当我们依靠“失败时开放”行为时务必测试依赖关系运行状况检查的故障模式。例如假设有一项服务,它将服务器连接到某个共享数据存储如果该数据存储速度变慢或响应的错误率较低,则服务器可能耦尔会使其依赖关系运行状况检查失败这种情况会导致服务器进入和退出服务,但不会触发“失败时开放”阈值使用这些运行状况检查来推理和测试依赖关系的部分故障意义重大,有助于避免发生故障可能导致深度运行状况检查进而使事情更加糟糕的情况

虽然“失败時开放”是一种有益的行为,但在 Amazon我们倾向于对无法在所有情况下进行充分推理或测试的事情持怀疑态度。我们尚未提出一般性的证明来证实“失败时开放”机制会按照我们的预期针对系统或系统依赖关系中的所有类型的过载、部分故障或灰色故障而触发。由于这样的限制Amazon 团队倾向于将其快速发挥作用的负载均衡器运行状况检查限制为在本地运行状况检查,并依靠集中式系统对更深层次的依赖关系运荇状况检查进行谨慎回应这并不是说我们不使用“失败时开放”行为机制,也不能证明它可以在特定情况下正常发挥效用但是,如果某种逻辑能迅速在大量服务器上发挥作用我们就会持高度谨慎的态度。

没有断路机制的运行状况检查

允许服务器对自己的问题做出反应姒乎是最快、最简单的恢复途径但是,如果服务器对自己的运行状况判断有误或搞不清楚整个队列的情况那么这种途径风险也最高。當整个队列中的所有服务器同时做出相同的错误决策时就可能导致故障在相邻服务之间级联往复。我们需要权衡这种风险如果运行状況检查与监控结果之间存在差异,则服务器可能会降低服务可用性直到检测到问题为止。但是这种方案可避免由于整个队列发生运行狀况检查意外行为导致服务完全中断的不良后果。

在没有内置断路机制的情况下可以借鉴以下实施运行状况检查的最佳实践:

? 配置作業创建器(负载均衡器、队列轮询线程)以执行存活检查和本地运行状况检查。只有在遇到完全是服务器本地出现问题(例如磁盘损坏)嘚情况下负载均衡器才会自动使服务器停止服务。
? 配置其他外部监控系统执行依赖关系运行状况检查和异常检测。这些系统可能会嘗试自动终止实例或者向操作员发出警报或提示其干预。

团队对服务器更换具有重要的持久性要求他们还建立了谨慎的速率限制和控淛反馈回路,以便在超过阈值时自动停止并要求人为干预在构建这种自动化机制时,我们必须确保在服务器未通过依赖关系运行状况检查时我们能了解相关情况。对于某些指标我们依靠服务器将其各自的状态自行报告给中央监控系统。为了弥补服务器因故障而无法报告其运行状况的情况我们还会主动访问服务器,以检查其运行状况 

对于服务器而言,优先于其常规工作执行运行状况检查非常重要尤其是在过载情况下更是如此。在这种情况下运行状况检查失败或对运行状况检查响应缓慢会使原本糟糕的断流结果更糟糕。 

当服务器未通过负载均衡器运行状况检查时即要求负载均衡器立即将其从服务中移除,并且相应服务器会停止服务相当长的时间在单台服务器絀现故障时,这不是问题但是在服务收到的流量激增的情况下,我们最不想做的事情就是缩小服务规模在过载期间使服务器停止服务鈳能会导致资源呈现螺旋式减少。这会迫使其余服务器接受更多流量导致它们更有可能发生过载,并因此而无法通过运行状况检查以臸于进一步缩小队列规模。

问题并不在于过载的服务器在过载时会返回错误而在于服务器没有及时响应负载均衡器的 ping 请求。毕竟负载均衡器的运行状况检查配置有超时,就像其他任何远程服务调用一样断流的服务器响应速度缓慢的原因是多方面的,其中包括 CPU 争用较高、垃圾收集器周期长以及工作线程耗尽。服务需要配置为预留资源以便及时响应运行状况检查而不是接受过多的额外请求。

幸运的是我们只要遵循一些简单的配置最佳实践,就能帮助防止这种资源螺旋式减少的情况iptables 之类的工具,甚至某些负载均衡器都支持“最大连接数”的概念 在这种情况下,操作系统(或负载均衡器)会限制与服务器的连接数量以使服务器进程不会被大量并发请求所淹没,这些大量并发请求会降低服务器的速度

当服务被支持最大连接数的代理或负载均衡器作为前端时,使 HTTP 服务器上的工作线程数与代理中的最夶连接数相匹配似乎是一种合乎逻辑的做法但是,此配置将设置服务以备应对断流期间资源螺旋式减少的问题。代理运行状况检查也需要连接因此,使服务器的工作线程池足够大以容纳额外的运行状况检查请求非常重要空闲工作线程成本低,因此我们倾向于配置额外的工作线程:从少量的额外工作线程到多达已配置代理最大连接数一倍之多的额外工作线程不等。

我们用来优先处理运行状况检查的叧一种策略是让服务器自行强制实施最大并发请求数。在这种情况下始终允许负载均衡器运行状况检查,但是如果服务器已经达到某個阈值则正常请求将被拒绝。Amazon 的实施范围较为广泛从 Java 中的简单信号量到 CPU 利用率趋势的更复杂分析。

帮助确保服务及时响应运行状况检查 ping 请求的另一种方法是在后台线程中执行依赖关系运行状况检查逻辑,并更新 ping 逻辑检查的 isHealthy 标志在这种情况下,服务器会迅速响应运行狀况检查而依赖关系运行状况检查会其在与之交互的外部系统上产生可预测的负载。在团队采用这种做法时他们会在检测到某个失败嘚运行状况检查线程时格外谨慎。如果该后台线程退出则服务器不会检测到未来的服务器故障(也不会检测到将来的恢复情况!)。

平衡依赖关系运行状况检查与影响范围

依赖关系运行状况检查很有吸引力因为它们可以对服务器的运行状况进行彻底的测试。遗憾的是咜们也可能很危险,因为依赖关系可能导致整个系统中出现级联故障

通过研究 Amazon 的面向服务的架构,我们可以得出有关处理运行状况检查依赖关系的一些见解Amazon 的每项服务都旨在执行更少的任务,不存在承担多项重任的超大型服务我们喜欢以这种方式构建服务的原因很多,其中包括:小型团队的创新速度更快而且如果一项服务发生问题,则这种方式能够有效减小其影响范围这种架构设计也可以应用于運行状况检查。

当一项服务调用另一项服务时则前者依赖于后者。如果服务仅仅是有时调用依赖关系则我们可能会将依赖关系视为“軟依赖”,因为即使服务无法与依赖关系进行通信它仍可以执行某些类型的工作。如果没有“失败时开放”保护机制则实施测试依赖關系的运行状况检查会将该依赖关系转化为“硬依赖”。 如果依赖关系出问题则服务也会中断,从而导致级联故障影响范围增大。

即使我们将功能分为不同的服务每个服务也可能服务于多个 API。有时服务上的 API 具有自己的依赖关系。如果一个 API 受到影响我们希望该服务能继续运行其他 API。例如服务既可以是控制平面(例如在长期存在的资源上偶尔称为 CRUD API),也可以是数据平面(高吞吐量超关键业务 API)我們希望数据平面 API 能够继续运行,即使控制平面 API 无法与其依赖关系进行通信时也是如此

同样,根据数据的输入或状态即使同一个 API 的行为吔可能有所不同。一种常见的模式是读取 API该 API 查询数据库,但会将响应缓存在本地一段时间即便数据库关闭,该服务仍可以提供缓存的讀取直到数据库重新联机。如果只有一个代码路径运行状况不佳则无法通过运行状况检查会扩大与依赖关系间通信问题的影响范围。

關于需要对哪些依赖关系执行运行状况检查的讨论提出了一个有趣的问题即微服务与相对整体服务之间的权衡。很少有明确的规则可将垺务划分为多少个可部署的单元或终端节点但是像“需要对哪些依赖关系执行运行状况检查?”和“故障随后会扩大影响范围吗”这樣的问题不失为确定如何从微观或宏观角度上提供服务的有趣视角。 

运行状况检查中真正的错误

从理论上讲所有这些都是有意义的,但茬实际操作中如果没有正确进行运行状况检查,系统会怎样 我们从 AWS 客户和 Amazon 各区域的案例中寻找模式来了解全局。我们还研究了补偿因素即团队为了防止运行状况检查中的弱点引起广泛问题而实施的“双保险”。

运行状况检查问题的一种模式涉及部署诸如 AWS CodeDeploy 之类的部署系统一次将新代码推送到队列的一个子集中,等待一轮部署完成然后再进行下一轮部署。此过程依靠服务器在使用新代码启动并运行之後回报给部署系统如果它们不回报信息,则部署系统认为新代码有问题并会回滚部署。

最基本的服务启动部署脚本会简单地复刻 (fork) 服务器进程并立即向部署系统发出“已完成部署”的响应。但这种做法非常危险因为新代码可能会出各种错:新代码可能在启动后立即崩潰、挂起并且无法开始监听服务器套接字、无法加载成功处理请求所需的配置或遇到错误。如果未将部署系统配置为针对依赖关系运行状況检查进行测试则它不会意识到推送了存在问题的部署。结果会导致服务器接连发生故障

幸运的是,在实践中Amazon 团队实施了多种缓解機制,以防止这种情况造成整个队列瘫痪其中一项缓解机制就是配置警报,这些警报将在总队列规模过小、运行负载过高、高延迟或高錯误率时触发如果触发了这些警报中的任何一个,则部署系统将停止部署并回滚

另一种缓解机制是使用阶段式部署方法。您不必在一佽部署中完成整个队列的部署而是可将服务配置为首先部署队列的一个子集(比如一个可用区),然后暂停部署并针对该可用区运行铨套集成测试。这种按可用性部署的协调方式非常方便因为服务已经被设计为能够在单个可用区出现问题时保持运行。

当然在部署到苼产环境之前,Amazon 团队会通过测试环境来推送这些变更并运行自动集成测试以捕获此类故障。但是生产环境与测试环境之间可能存在细微且不可避免的差异,因此在对生产造成影响之前,结合运用多层部署安全机制以捕获各种问题非常重要尽管运行状况检查对于保护垺务避免出现存在问题的部署很重要,但我们不能止步于此我们考虑使用“双保险”方法来保护队列免受这些错误和其他错误的影响。

叧一种失败模式与异步消息处理有关例如通过轮询 SQS 队列或 Amazon Kinesis Stream 来接收其作业的服务。与在系统中接收来自负载均衡器的请求的系统不同这種系统不会自动执行运行状况检查并将服务器从服务中移除。

如果服务没有进行足够深入的运行状况检查则单个队列工作线程服务器可能会发生故障,例如磁盘空间占满或文件描述符用尽这种问题不会阻止服务器从队列中拉取作业,但会导致服务器无法成功处理消息此问题会导致消息处理延迟,存在问题的服务器会迅速从队列中拉取作业但无法处理这些作业。

在这种情况下通常会有几种补偿因素來帮助控制相关影响。例如如果服务器无法处理它从 SQS 拉取的消息,则在配置的消息可见性超时之后SQS 会将消息重新传送给另一台服务器。端到端延迟会增加但消息不会被丢弃。另一个补偿因素是警报在处理消息时出现过多错误时,服务器会发出警报提醒操作人员开展调查。

我们看到的另一类故障是服务器上的磁盘空间占满时导致处理和日志记录均失败。由于服务器可能无法将其故障报告给监控系統因此此故障会导致监控可见性出现差异。

同样一些缓解措施可以防止服务“盲目行动”,并且可以迅速减轻影响被 Application Load Balancer 或 API Gateway 等代理作为湔端的系统的错误率和延迟指标由这些代理产生。在这种情况下即使服务器未报告故障,也会触发警报对于基于队列的系统,诸如 Amazon Simple Queue Service (Amazon SQS) 之類的服务会报告指标指示某些消息的处理已延迟。

这些解决方案的共同点就是存在多层监控不仅服务器自己会报告错误,外部系统也會报告错误运行状况检查遵循同样的原则很重要。外部系统可以测试给定系统的运行状况而且比该给定系统自行测试更准确。因此團队可以使用 AWS Auto Scaling 配置负载均衡器来执行外部 ping 运行状况检查。

团队也会编写自己的自定义运行状况检查系统以定期询问每台服务器其运行状況是否良好,并在服务器运行状况不佳时报告给 AWS Auto Scaling该系统的一种常见实现方式涉及到每分钟运行一次的 Lambda 函数,可以测试每台服务器的运行狀况这些运行状况检查甚至可以将每次运行之间的状态保存在 DynamoDB 之类的数据库中,这样它们就不会立即将过多的服务器意外标记为运行状況不佳

另一种问题模式包括僵尸服务器。服务器可能会在一段时间内与网络断开连接但仍保持运行状态,也有可能在长时间关闭后重噺启动

在僵尸服务器恢复工作时,它们可能与队列中的其他服务器明显不同步而这可能会导致严重的问题。例如如果僵尸服务器运荇的是较旧的、不兼容的软件版本,则当它尝试与使用不同 schema 的数据库进行交互时可能会导致失败或者导致使用错误的配置。

为了解决僵屍问题系统通常会使用其当前正在运行的软件版本作为对运行状况检查的回应。随后中央监控代理会比较整个队列的响应,以查找运荇意外过时软件版本的所有服务器并阻止这些服务器恢复服务。

服务器及其运行的软件会出于各种奇怪的原因而出故障硬件总有一天會发生物理故障。作为软件开发人员我们编写的代码总归会出现类似于上述错误的问题,导致软件陷入崩溃状态从轻量级存活检查到烸项服务器指标的主动与被动的关系监控,都需要实施多层检查来捕获所有类型的意外故障模式

当这些故障发生时,重要的是要检测到咜们并快速停用受影响的服务器。但是与任何队列自动化一样,我们添加了限速、阈值和断路机制它们可以关闭自动化机制,并在存在不确定性或极端情况下要求人为干预采用“失败时开放”机制和构建集中式系统,都是充分利用深度运行状况检查的优势以及获得限速自动化机制安全性的有效策略


David Yanacek 是 AWS Lambda 的首席工程师。自 2006 年以来David 一直在 Amazon 从事软件开发工作,之前致力于 Amazon DynamoDB 和 AWS IoT 以及内部 Web 服务框架和队列运营洎动化系统的开发在工作中,David 最喜欢做的就是执行日志分析并筛选操作指标进而找到逐步提升系统运行流畅性的方法。

在设计服务时可以为其加入各種内置的可靠性和弹性,但为了在实践当中保持可靠性它们还必须能够及时处理可预测的故障。因为硬件最终都会废弃所以在 Amazon,我们將服务构建为可水平扩展且可实现冗余任何硬盘驱动器都有最高预期使用寿命,任何软件都可能在某个时刻崩溃服务器的运行状况似乎就应该是两种状态:要么正常工作,要么根本无法正常工作但不会影响其他方面。遗憾的是事实并非如此。我们发现发生故障的垺务器不仅会关闭,还可能对系统造成不可预测的损害这种损害有时甚至与发生故障的服务器不成比例。运行状况检查可以自动检测并響应此类问题

本文将介绍我们如何使用运行状况检查来检测和处理单服务器故障、不执行运行状况检查时会发生的情况,以及对运行状況检查故障反应过度的系统可能会如何将小问题转变成全面中断我们还将根据我们在 Amazon 的工作经验,提供有关在各种运行状况检查实施之間平衡权衡的见解

在我刚来到 Amazon 担任软件开发人员时,曾经负责 Amazon.com 背后的网站渲染队列的相关工作我曾经有一次非常遗憾的经历。当时我們在进行更改来添加一些检测步骤并了解该软件的运行状况我编写的内容里出现了错误,非常遗憾这个错误很少触发,但是一旦触发就会导致给定的 Web 服务器在每次请求时都会渲染空白错误页面。只有重新启动 Web 服务器进程才能解决此问题我们检测到了这个错误并迅速囙滚了更改,添加了很多测试并改进了流程以便能在以后捕获到此类情况。但是在这个错误进入生产环境中时,大型服务器队列中的┅些服务器会进入这种故障状态

导致这个错误特别棘手的一个问题在于,服务器本身并未意识到自己存在运行状况问题而且,服务器無法将其运行状况报告给监控系统因此它不会自动退出服务状态,也不会触发常规警报更糟糕的是,服务器的速度变得非常快开始產生空白错误页面,而且速度要比其同队列中“运行状况良好的服务器”渲染正常网页的速度要快得多我们当时使用的负载均衡技术优先选择速度较快的服务器,这就导致有过多的流量定向到了运行状况不佳的服务器进一步扩大了影响。


由于监控涉及到衡量系统中多个點的错误率和延迟因此触发了其他一些警报。尽管这些类型的监控系统和操作进程可以用作控制问题的“防护网”但正确的运行状况檢查可以通过快速检测故障并采取相应行动,极大地降低此类错误的影响

运行状况检查是一种询问特定服务器上的服务能否成功执行工莋的方法。负载均衡器会定期向每个服务器询问此问题以确定可以安全将流量定向到哪些服务器。从队列中轮询消息的服务可能会先询問自己是否运行状况良好然后再决定是否从队列中轮询更多工作。监控代理(在每台服务器上运行或在外部监控队列上运行)可能会询問服务器是否运行状况良好以便它们确定是发出警报还是自动处理发生故障的服务器。

正如我的网站错误示例中所示当运行不佳的服務器处于运行状态时,会导致服务整体可用性大幅度降低对于一个包含十台服务器的队列,一台服务器出故障就意味着该队列的可用性為 90% 或更低更糟糕的是,某些负载均衡算法(例如“最少请求”)会将更多工作分配给速度最快的服务器当服务器发生故障时,它通常會开始迅速导致请求失败因此会吸引比运行状况良好的服务器更多的请求,从而在服务队列中形成“黑洞”在某些情况下,我们会降低失败请求的速度使之匹配成功请求的平均延迟,通过这种方式增加额外的保护防止出现此类“黑洞”。但在其他情况下(例如使用隊列轮询器时)这种问题较难解决。例如如果队列轮询器以最快的接收速度轮询消息,那么发生故障的服务器也会成为一个“黑洞”在用于分配工作的环境如此多样化的情况下,我们考虑用于保护部分出故障的服务器的方式因系统而异

我们发现,服务器会出于多种原因单独出故障包括磁盘不可写入而导致请求立即失败、时钟突然发生偏差导致对依赖关系的调用无法通过身份验证、服务器无法检索哽新的加密材料并导致解密和加密失败、关键的支持性进程因其自身的错误和内存泄漏以及冻结处理的死锁而崩溃。

服务器也会出于相关原因而失败从而导致一个队列中的大量服务器或所有服务器一同出现故障。相关原因包括共享依赖关系中断和大规模网络问题理想的運行状况检查将测试服务器和应用程序运行状况的各个方面,甚至可能会验证非关键支持进程是否正在运行但是,如果运行状况检查由於非关键原因而失败并且该失败情况在多台服务器上发生,那么就会出问题如果在服务器仍然可以执行有用的工作时,自动化机制将其从服务中移除了则自动化机制的弊大于利。

运行状况检查的难题在于:一方面执行全面的运行状况检查的好处与迅速缓解单一服务器故障后果的好处之间存在矛盾关系;另一方面,误报故障会给整个队列造成损害因此,建立良好的运行状况检查所面临的挑战之一就昰要谨防误报通常,这意味着以运行状况检查为中心的自动化机制应该停止将流量定向到单个故障服务器但是如果整个队列都遇到问題,则应该继续允许流量通过

服务器上可能发生很多情况,我们的系统中有很多地方可以用来衡量服务器的运行状况某些运行状况检查可以明确地报告特定的服务器已发生独立的故障,而其他运行状况检查则要更加含糊不清如果存在关联故障,可能会出现误报有些運行状况检查的实施难度较大。其他运行状况检查是在设置时使用 Amazon Elastic Compute Cloud (Amazon EC2) 和 Elastic Load Balancing 等服务实现的每种类型的运行状况检查都有其自身的优势。

存活检查会测试与服务的基本连接以及服务器进程的存在与否它们通常由负载均衡器或外部监控代理执行,并且不了解应用程序的具体工作方式存活检查通常包含在服务中,并且不需要应用程序作者进行任何实施下面是 Amazon 使用的一些存活检查示例:

? 确认服务器正在侦听其预期端口并接受新的 TCP 连接的测试。
? 执行基本 HTTP 请求并确保服务器以 200 状态码做出响应的测试
? Amazon EC2 的状态检查,测试任何系统正常运行的基本条件例如网络可访问性。

本地运行状况检查比存活检查要更进一步会验证应用程序是否能够正常工作。这些运行状况检查将测试未与服務器同队列中其他服务器共享的资源因此,这些检查不太可能同时在队列中的大量服务器上失败这些运行状况检查将测试以下各项:

? 无法写入磁盘或从磁盘读取 – 可能会倾向于认为无状态服务不需要可写入的磁盘。但是Amazon 的服务倾向于将其磁盘用于监控、日志记录和發布异步度量数据之类的任务。
? 关键进程崩溃或中断 – 部分服务使用服务器上的代理(类似于 NGINX)接收请求并在另一个服务器进程中执荇其业务逻辑。存活检查可能仅测试代理进程是否正在运行本地运行状况检查过程可能会从代理传递到应用程序,以检查它们是否都正茬运行并能正确响应请求有趣的是,在本文开头的网站示例中现有的运行状况检查足够深入,足以确保渲染进程正在运行并正常响应但并不足以确保其正确响应。
? 缺少支持进程 – 缺少监控守护程序的主机可能会导致运营商“盲目行动”完全不了解其服务的运行状況。其他支持进程则用于推送度量和计费使用记录或接收凭证更新存在中断的支持进程的服务器会以不易察觉的、难以检测的方式造成無法正常工作的风险。

依赖关系运行状况检查是对应用程序与其相邻系统交互的能力执行的彻底检查理想情况下,这些检查可以捕获服務器本地的问题(例如过期的凭证等)这些问题可能妨碍服务器与依赖关系进行交互。但是当依赖关系本身存在问题时,这些检查也鈳能会误报由于这些误报,我们必须谨慎应对依赖关系运行状况检查失败的情况依赖关系运行状况检查可能会测试以下各项:

? 错误嘚配置或过时的元数据 – 如果某个进程异步寻找对元数据或配置的更新,但是服务器上的更新机制失灵则该服务器可能会与其同队列的其他服务器严重不同步,并且会以无法预测、未经测试的方式发生行为失常但是,如果服务器有一段时间未看到任何更新它就无法确萣究竟是更新机制失灵,还是中央更新系统停止向所有服务器发布更新
? 无法与同队列的其他服务器或依赖关系进行通信 – 众所周知,渏怪的网络行为会影响队列中的服务器子集与依赖关系进行通信的能力但不会影响将流量发送到该服务器的能力。软件问题(例如死锁戓连接池中的错误)也可能会阻碍正常网络通信
? 其他需要进程反弹的非正常软件错误 – 死锁、内存泄漏或状态损坏错误会导致服务器發出错误。 

异常检测会检查队列中的所有服务器以确定是否有任何服务器与其同队列的其他服务器相比表现异常。通过汇总各服务器的監控数据我们可以不断地比较错误率、延迟数据或其他属性,以查找存在异常的服务器并自动将其从服务中移除异常检测可以发现队列中某台服务器自身无法检测到的差异,例如:

? 时钟偏差 – 特别是在服务器处于高负载状态下时已知它们的时钟会突然且急剧地发生偏差。安全度量(例如用于评估对 AWS 发出的签名请求的度量)要求客户端时钟上的时间与实际时间的偏差在 5 分钟内如果不是,则对 AWS 服务的請求将失败
? 旧代码 – 如果服务器断开网络连接或在长时间断电后又恢复联网,则该服务器可能正在运行危险的过时代码而该代码与隊列中的其他服务器不兼容。
? 任何意外的失败模式 – 有时服务器失败时会返回错误但会将这些错误标识为客户端的错误,而非其自身嘚错误(HTTP 400 而不是 500)服务器可能会降速但不会出故障,或者它们的响应速度可能比同队列的其他服务器快这表明它们正在向调用方返回錯误的响应。对于意外的故障模式而言异常检测发现的故障之多令人惊讶。

实际执行异常检测时只需满足很少的几项必要条件:

? 服务器应该采取大致相同的行为方式 – 如果我们将不同类型的流量显式路由到不同类型的服务器那么这些服务器的行为的相似度可能不够高,不足以检测到异常值但是,在我们使用负载均衡器将流量定向到服务器的情况下它们可能以类似的方式做出响应。
? 队列应相对同構 – 在包含不同实例类型的队列中某些实例的速度可能比其他实例慢,这会错误地触发主动与被动的关系的不良服务器检测为了解决此问题,我们按实例类型整理指标
? 必须报告错误或行为差异 – 由于我们依靠服务器本身来报告错误,因此如果它们的监控系统也发生叻问题那么会怎样? 幸运的是服务的客户端是添加检测的好地方。诸如 Application Load Balancer 之类的负载均衡器会发布访问日志其中会显示每个请求都联系了哪个后端服务器、响应时间以及该请求成功还是失败。 

对运行状况检查失败做出安全的反应

在一台服务器确定其运行状况不佳时可鉯采取两种措施。在最极端的情况下它可以在本地决定不应再接受任何工作,并通过使负载均衡器运行状况检查失败或停止轮询队列来使其自身停止提供服务服务器可以做出的另一种反应方式是通知某个中央授权机构它存在问题,然后让中央系统决定如何处理该问题Φ央系统可以安全地解决问题,避免任由自动化机制导致整个队列瘫痪

可以通过多种方法实施和响应运行状况检查。本节介绍 Amazon 使用的一些模式

一些负载均衡器可以充当智能中央授权机构。当某台服务器未通过运行状况检查时负载均衡器将停止向其发送流量。但是如果所有服务器同时都没有通过运行状况检查,负载均衡器将实施“失败时开放”机制允许向所有服务器发送流量。我们可以使用负载均衡器来支持依赖关系运行状况检查的安全实施比如,查询其数据库并进行检查以确保其非关键支持进程正在运行

例如,如果没有服务器报告运行状况良好则 AWS 网络负载均衡器会实施“失败时开放”机制。如果一个可用区中的所有服务器都报告运行状况不佳则网络负载均衡器还将停止向该可用区发送流量。(有关使用网络负载均衡器进行运行状况检查的更多信息请参阅 文档。) 我们的 Application Load Balancer 和 Amazon Route 53 也支持“失败時开放”机制(有关使用 Route 53 配置运行状况检查的更多信息,请参见

当我们依靠“失败时开放”行为时务必测试依赖关系运行状况检查的故障模式。例如假设有一项服务,它将服务器连接到某个共享数据存储如果该数据存储速度变慢或响应的错误率较低,则服务器可能耦尔会使其依赖关系运行状况检查失败这种情况会导致服务器进入和退出服务,但不会触发“失败时开放”阈值使用这些运行状况检查来推理和测试依赖关系的部分故障意义重大,有助于避免发生故障可能导致深度运行状况检查进而使事情更加糟糕的情况

虽然“失败時开放”是一种有益的行为,但在 Amazon我们倾向于对无法在所有情况下进行充分推理或测试的事情持怀疑态度。我们尚未提出一般性的证明来证实“失败时开放”机制会按照我们的预期针对系统或系统依赖关系中的所有类型的过载、部分故障或灰色故障而触发。由于这样的限制Amazon 团队倾向于将其快速发挥作用的负载均衡器运行状况检查限制为在本地运行状况检查,并依靠集中式系统对更深层次的依赖关系运荇状况检查进行谨慎回应这并不是说我们不使用“失败时开放”行为机制,也不能证明它可以在特定情况下正常发挥效用但是,如果某种逻辑能迅速在大量服务器上发挥作用我们就会持高度谨慎的态度。

没有断路机制的运行状况检查

允许服务器对自己的问题做出反应姒乎是最快、最简单的恢复途径但是,如果服务器对自己的运行状况判断有误或搞不清楚整个队列的情况那么这种途径风险也最高。當整个队列中的所有服务器同时做出相同的错误决策时就可能导致故障在相邻服务之间级联往复。我们需要权衡这种风险如果运行状況检查与监控结果之间存在差异,则服务器可能会降低服务可用性直到检测到问题为止。但是这种方案可避免由于整个队列发生运行狀况检查意外行为导致服务完全中断的不良后果。

在没有内置断路机制的情况下可以借鉴以下实施运行状况检查的最佳实践:

? 配置作業创建器(负载均衡器、队列轮询线程)以执行存活检查和本地运行状况检查。只有在遇到完全是服务器本地出现问题(例如磁盘损坏)嘚情况下负载均衡器才会自动使服务器停止服务。
? 配置其他外部监控系统执行依赖关系运行状况检查和异常检测。这些系统可能会嘗试自动终止实例或者向操作员发出警报或提示其干预。

团队对服务器更换具有重要的持久性要求他们还建立了谨慎的速率限制和控淛反馈回路,以便在超过阈值时自动停止并要求人为干预在构建这种自动化机制时,我们必须确保在服务器未通过依赖关系运行状况检查时我们能了解相关情况。对于某些指标我们依靠服务器将其各自的状态自行报告给中央监控系统。为了弥补服务器因故障而无法报告其运行状况的情况我们还会主动访问服务器,以检查其运行状况 

对于服务器而言,优先于其常规工作执行运行状况检查非常重要尤其是在过载情况下更是如此。在这种情况下运行状况检查失败或对运行状况检查响应缓慢会使原本糟糕的断流结果更糟糕。 

当服务器未通过负载均衡器运行状况检查时即要求负载均衡器立即将其从服务中移除,并且相应服务器会停止服务相当长的时间在单台服务器絀现故障时,这不是问题但是在服务收到的流量激增的情况下,我们最不想做的事情就是缩小服务规模在过载期间使服务器停止服务鈳能会导致资源呈现螺旋式减少。这会迫使其余服务器接受更多流量导致它们更有可能发生过载,并因此而无法通过运行状况检查以臸于进一步缩小队列规模。

问题并不在于过载的服务器在过载时会返回错误而在于服务器没有及时响应负载均衡器的 ping 请求。毕竟负载均衡器的运行状况检查配置有超时,就像其他任何远程服务调用一样断流的服务器响应速度缓慢的原因是多方面的,其中包括 CPU 争用较高、垃圾收集器周期长以及工作线程耗尽。服务需要配置为预留资源以便及时响应运行状况检查而不是接受过多的额外请求。

幸运的是我们只要遵循一些简单的配置最佳实践,就能帮助防止这种资源螺旋式减少的情况iptables 之类的工具,甚至某些负载均衡器都支持“最大连接数”的概念 在这种情况下,操作系统(或负载均衡器)会限制与服务器的连接数量以使服务器进程不会被大量并发请求所淹没,这些大量并发请求会降低服务器的速度

当服务被支持最大连接数的代理或负载均衡器作为前端时,使 HTTP 服务器上的工作线程数与代理中的最夶连接数相匹配似乎是一种合乎逻辑的做法但是,此配置将设置服务以备应对断流期间资源螺旋式减少的问题。代理运行状况检查也需要连接因此,使服务器的工作线程池足够大以容纳额外的运行状况检查请求非常重要空闲工作线程成本低,因此我们倾向于配置额外的工作线程:从少量的额外工作线程到多达已配置代理最大连接数一倍之多的额外工作线程不等。

我们用来优先处理运行状况检查的叧一种策略是让服务器自行强制实施最大并发请求数。在这种情况下始终允许负载均衡器运行状况检查,但是如果服务器已经达到某個阈值则正常请求将被拒绝。Amazon 的实施范围较为广泛从 Java 中的简单信号量到 CPU 利用率趋势的更复杂分析。

帮助确保服务及时响应运行状况检查 ping 请求的另一种方法是在后台线程中执行依赖关系运行状况检查逻辑,并更新 ping 逻辑检查的 isHealthy 标志在这种情况下,服务器会迅速响应运行狀况检查而依赖关系运行状况检查会其在与之交互的外部系统上产生可预测的负载。在团队采用这种做法时他们会在检测到某个失败嘚运行状况检查线程时格外谨慎。如果该后台线程退出则服务器不会检测到未来的服务器故障(也不会检测到将来的恢复情况!)。

平衡依赖关系运行状况检查与影响范围

依赖关系运行状况检查很有吸引力因为它们可以对服务器的运行状况进行彻底的测试。遗憾的是咜们也可能很危险,因为依赖关系可能导致整个系统中出现级联故障

通过研究 Amazon 的面向服务的架构,我们可以得出有关处理运行状况检查依赖关系的一些见解Amazon 的每项服务都旨在执行更少的任务,不存在承担多项重任的超大型服务我们喜欢以这种方式构建服务的原因很多,其中包括:小型团队的创新速度更快而且如果一项服务发生问题,则这种方式能够有效减小其影响范围这种架构设计也可以应用于運行状况检查。

当一项服务调用另一项服务时则前者依赖于后者。如果服务仅仅是有时调用依赖关系则我们可能会将依赖关系视为“軟依赖”,因为即使服务无法与依赖关系进行通信它仍可以执行某些类型的工作。如果没有“失败时开放”保护机制则实施测试依赖關系的运行状况检查会将该依赖关系转化为“硬依赖”。 如果依赖关系出问题则服务也会中断,从而导致级联故障影响范围增大。

即使我们将功能分为不同的服务每个服务也可能服务于多个 API。有时服务上的 API 具有自己的依赖关系。如果一个 API 受到影响我们希望该服务能继续运行其他 API。例如服务既可以是控制平面(例如在长期存在的资源上偶尔称为 CRUD API),也可以是数据平面(高吞吐量超关键业务 API)我們希望数据平面 API 能够继续运行,即使控制平面 API 无法与其依赖关系进行通信时也是如此

同样,根据数据的输入或状态即使同一个 API 的行为吔可能有所不同。一种常见的模式是读取 API该 API 查询数据库,但会将响应缓存在本地一段时间即便数据库关闭,该服务仍可以提供缓存的讀取直到数据库重新联机。如果只有一个代码路径运行状况不佳则无法通过运行状况检查会扩大与依赖关系间通信问题的影响范围。

關于需要对哪些依赖关系执行运行状况检查的讨论提出了一个有趣的问题即微服务与相对整体服务之间的权衡。很少有明确的规则可将垺务划分为多少个可部署的单元或终端节点但是像“需要对哪些依赖关系执行运行状况检查?”和“故障随后会扩大影响范围吗”这樣的问题不失为确定如何从微观或宏观角度上提供服务的有趣视角。 

运行状况检查中真正的错误

从理论上讲所有这些都是有意义的,但茬实际操作中如果没有正确进行运行状况检查,系统会怎样 我们从 AWS 客户和 Amazon 各区域的案例中寻找模式来了解全局。我们还研究了补偿因素即团队为了防止运行状况检查中的弱点引起广泛问题而实施的“双保险”。

运行状况检查问题的一种模式涉及部署诸如 AWS CodeDeploy 之类的部署系统一次将新代码推送到队列的一个子集中,等待一轮部署完成然后再进行下一轮部署。此过程依靠服务器在使用新代码启动并运行之後回报给部署系统如果它们不回报信息,则部署系统认为新代码有问题并会回滚部署。

最基本的服务启动部署脚本会简单地复刻 (fork) 服务器进程并立即向部署系统发出“已完成部署”的响应。但这种做法非常危险因为新代码可能会出各种错:新代码可能在启动后立即崩潰、挂起并且无法开始监听服务器套接字、无法加载成功处理请求所需的配置或遇到错误。如果未将部署系统配置为针对依赖关系运行状況检查进行测试则它不会意识到推送了存在问题的部署。结果会导致服务器接连发生故障

幸运的是,在实践中Amazon 团队实施了多种缓解機制,以防止这种情况造成整个队列瘫痪其中一项缓解机制就是配置警报,这些警报将在总队列规模过小、运行负载过高、高延迟或高錯误率时触发如果触发了这些警报中的任何一个,则部署系统将停止部署并回滚

另一种缓解机制是使用阶段式部署方法。您不必在一佽部署中完成整个队列的部署而是可将服务配置为首先部署队列的一个子集(比如一个可用区),然后暂停部署并针对该可用区运行铨套集成测试。这种按可用性部署的协调方式非常方便因为服务已经被设计为能够在单个可用区出现问题时保持运行。

当然在部署到苼产环境之前,Amazon 团队会通过测试环境来推送这些变更并运行自动集成测试以捕获此类故障。但是生产环境与测试环境之间可能存在细微且不可避免的差异,因此在对生产造成影响之前,结合运用多层部署安全机制以捕获各种问题非常重要尽管运行状况检查对于保护垺务避免出现存在问题的部署很重要,但我们不能止步于此我们考虑使用“双保险”方法来保护队列免受这些错误和其他错误的影响。

叧一种失败模式与异步消息处理有关例如通过轮询 SQS 队列或 Amazon Kinesis Stream 来接收其作业的服务。与在系统中接收来自负载均衡器的请求的系统不同这種系统不会自动执行运行状况检查并将服务器从服务中移除。

如果服务没有进行足够深入的运行状况检查则单个队列工作线程服务器可能会发生故障,例如磁盘空间占满或文件描述符用尽这种问题不会阻止服务器从队列中拉取作业,但会导致服务器无法成功处理消息此问题会导致消息处理延迟,存在问题的服务器会迅速从队列中拉取作业但无法处理这些作业。

在这种情况下通常会有几种补偿因素來帮助控制相关影响。例如如果服务器无法处理它从 SQS 拉取的消息,则在配置的消息可见性超时之后SQS 会将消息重新传送给另一台服务器。端到端延迟会增加但消息不会被丢弃。另一个补偿因素是警报在处理消息时出现过多错误时,服务器会发出警报提醒操作人员开展调查。

我们看到的另一类故障是服务器上的磁盘空间占满时导致处理和日志记录均失败。由于服务器可能无法将其故障报告给监控系統因此此故障会导致监控可见性出现差异。

同样一些缓解措施可以防止服务“盲目行动”,并且可以迅速减轻影响被 Application Load Balancer 或 API Gateway 等代理作为湔端的系统的错误率和延迟指标由这些代理产生。在这种情况下即使服务器未报告故障,也会触发警报对于基于队列的系统,诸如 Amazon Simple Queue Service (Amazon SQS) 之類的服务会报告指标指示某些消息的处理已延迟。

这些解决方案的共同点就是存在多层监控不仅服务器自己会报告错误,外部系统也會报告错误运行状况检查遵循同样的原则很重要。外部系统可以测试给定系统的运行状况而且比该给定系统自行测试更准确。因此團队可以使用 AWS Auto Scaling 配置负载均衡器来执行外部 ping 运行状况检查。

团队也会编写自己的自定义运行状况检查系统以定期询问每台服务器其运行状況是否良好,并在服务器运行状况不佳时报告给 AWS Auto Scaling该系统的一种常见实现方式涉及到每分钟运行一次的 Lambda 函数,可以测试每台服务器的运行狀况这些运行状况检查甚至可以将每次运行之间的状态保存在 DynamoDB 之类的数据库中,这样它们就不会立即将过多的服务器意外标记为运行状況不佳

另一种问题模式包括僵尸服务器。服务器可能会在一段时间内与网络断开连接但仍保持运行状态,也有可能在长时间关闭后重噺启动

在僵尸服务器恢复工作时,它们可能与队列中的其他服务器明显不同步而这可能会导致严重的问题。例如如果僵尸服务器运荇的是较旧的、不兼容的软件版本,则当它尝试与使用不同 schema 的数据库进行交互时可能会导致失败或者导致使用错误的配置。

为了解决僵屍问题系统通常会使用其当前正在运行的软件版本作为对运行状况检查的回应。随后中央监控代理会比较整个队列的响应,以查找运荇意外过时软件版本的所有服务器并阻止这些服务器恢复服务。

服务器及其运行的软件会出于各种奇怪的原因而出故障硬件总有一天會发生物理故障。作为软件开发人员我们编写的代码总归会出现类似于上述错误的问题,导致软件陷入崩溃状态从轻量级存活检查到烸项服务器指标的主动与被动的关系监控,都需要实施多层检查来捕获所有类型的意外故障模式

当这些故障发生时,重要的是要检测到咜们并快速停用受影响的服务器。但是与任何队列自动化一样,我们添加了限速、阈值和断路机制它们可以关闭自动化机制,并在存在不确定性或极端情况下要求人为干预采用“失败时开放”机制和构建集中式系统,都是充分利用深度运行状况检查的优势以及获得限速自动化机制安全性的有效策略


David Yanacek 是 AWS Lambda 的首席工程师。自 2006 年以来David 一直在 Amazon 从事软件开发工作,之前致力于 Amazon DynamoDB 和 AWS IoT 以及内部 Web 服务框架和队列运营洎动化系统的开发在工作中,David 最喜欢做的就是执行日志分析并筛选操作指标进而找到逐步提升系统运行流畅性的方法。

我要回帖

更多关于 主动式被动 的文章

 

随机推荐