你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

自我修复型设计

设计应用程序以在故障发生时进行自我修复

在分布式系统中,必须预料到故障的发生。 硬件可能发生故障。 网络也有可能发生暂时性故障。 整个服务、数据中心甚至 Azure 区域很少会出现中断;但是,即使是在工作负载体系结构中,也必须针对这些中断进行设计。 应在工作负载设计的早期解决复原和恢复问题。

因此,设计的应用程序在故障发生时可进行自我修复。 这需要从三个方面入手:

  • 检测故障。
  • 从容应对故障。
  • 记录和监视故障,以获取操作见解。

如何应对特定类型的故障取决于应用程序的可用性需求。 例如,如果需要高可用性,则可以部署到一个区域中的多个可用性区域。 即使整个 Azure 区域发生服务中断的概率极小,但要避免中断,可在发生区域性中断时自动故障转移到次要区域。 不过,与单区域部署相比,这将提高成本,并可能降低性能。

此外,不要只考虑像区域中断这类大事件,因为这种情况通常鲜有发生。 应该尽可能将注意力集中在处理本地短期的故障上,例如网络连接故障或数据库连接失败等。

建议

使用异步通信的分离组件。 理想情况下,组件在时间和空间方面分离。 时间上的分离意味着组件不需要同时存在,就可以进行通信。 空间上的分离意味着发送方和接收方不必在同一进程中运行,但只要效率更高,它们就可以在任何地方运行。 理想情况下,分离的组件使用事件相互通信。 这有助于将连锁故障的可能性降至最低。

重试失败的操作。 发生暂时性故障的原因可能有:短暂的网络连接中断、删除了数据库连接或服务因繁忙而超时。 在应用程序中构建重试逻辑来处理暂时性故障。 对于许多 Azure 服务,客户端 SDK 可实施自动重试。 有关详细信息,请参阅暂时性故障处理重试模式

保护故障远程服务(断路器)。 在暂时性故障后最好进行重试,但如果故障仍然存在,最终可能会有非常多的调用方攻击故障服务。 因为请求进行了备份,这可能导致连锁故障。 当操作可能失败时,使用断路器模式进行快速失败(不进行远程调用)。

隔离关键资源(隔层)。 子系统中的故障有时会发生级联。 如果某个故障导致某些资源(例如线程或套接字)无法及时释放,导致资源耗尽,则可能就会发生这种连锁反应。 为了避免这种情况,请使用隔舱模式将系统划分为独立的组,这样一个分区中的故障就不会导致整个系统瘫痪。

执行负载分级。 应用程序可能会遇到突发流量高峰,导致后端上的服务瘫痪。 为了避免此问题,请使用基于队列的负载调节模式使工作项排队进行异步运行。 队列充当可平缓负载高峰的缓冲区。

故障转移。 如果无法访问某个实例,请故障转移到另一个实例。 对于 Web 服务器之类的无状态对象,请在负载均衡器或流量管理器后放置一些实例。 对于数据库之类的存储状态的对象,请使用副本和故障转移。 根据数据存储和复制方式,应用程序可能必须处理最终的一致性。

补偿失败的事务。 一般情况下,需避免分布式事务,因为它们需要协调服务和资源。 相反,应该用较小的单个事务组成操作。 如果在中途操作失败,请使用补偿事务撤销已完成的所有步骤。

检查点长时间运行的事务。 如果长时间运行的操作失败,检查点可以提供复原能力。 当操作重新启动时(例如,它被另一个 VM 选中),它可以从上一个检查点恢复。 请考虑实现一种机制,用于定期记录任务的状态信息,并将此状态保存在运行任务的任何进程实例可以访问的持久性存储中。 这样,如果过程关闭,可以使用另一个实例从最后一个检查点继续进它所执行的工作。 有一些库提供此功能,例如 NServiceBusMassTransit。 它们以透明方式保留状态,其中间隔与 Azure 服务总线队列中的消息处理保持一致。

在故障情况下正常降级并保持快速响应。 有时某个问题无法解决,但可以提供仍然有用的缩减版功能。 假设某个应用程序显示图书目录。 如果该应用程序无法检索封面的缩略图图像,它可能显示占位符图像。 整个子系统可能对应用程序不重要。 例如,在电子商务网站,显示产品建议可能没有处理订单重要。

限制客户端。 有时,少量的用户会产生过多的负载,降低了应用程序对其他用户的可用性。 在这种情况下,可以在一段时间内限制客户端。 请参阅限制模式

阻止错误执行组件。 仅仅限制客户端并不意味着客户端的行为是恶意的。 它只意味着客户端超出其服务配额。 但如果客户端持续超出其配额或在其他方面具有不良行为,则可能需要进行阻止。 定义一个带外进程,供用户请求解除阻止。

使用领导选拔。 当需要协调任务时,请使用领导选拔选择协调器。 这样,协调器不是单一故障点。 如果协调器失败,则选择一个新的协调器。 与其从头开始实施领导选举算法,不如考虑现成的解决方案,比如 Zookeeper。

使用故障注入进行测试。 通常,成功的路径会得到精心的测试,而失败的路径却不会。 系统在生产中长时间运行后,才会执行失败路径。 通过触发实际故障或模拟故障,使用故障注入来测试系统对故障的复原能力。

采用混沌工程。 混沌工程通过将故障或异常情况随机注入到生产实例中,扩展了故障注入的概念。

使用可用性区域。 许多 Azure 区域提供可用性区域,这些可用性区域是区域内独立的数据中心集。 某些 Azure 服务可以按区域部署,这可确保它们放置在特定的区域中,并有助于减少同一工作负载中的组件之间通信的延迟。 或者,某些服务可以使用区域冗余进行部署,这意味着 Azure 会自动跨区域复制资源以实现高可用性。 考虑哪种方法可提供最佳解决方案。 若要详细了解如何设计解决方案以使用可用性区域和地区,请参阅 有关使用可用性区域和地区的建议

关于使应用程序自我修复的结构化方法,请参阅设计适用于 Azure 的可靠应用程序