自我修复型设计-常用的架构设计原则
设计应用程序应考虑系统在故障发生时能够进行自我修复
在分布式系统架构设计中,应用可能会发生故障、硬件可能发生故障、 网络也有可能发生暂时性故障。 极少数情况下,整个服务或区域可能会遇到中断,但这些故障必须在计划之内。
因此,我们设计的应用程序在故障发生时应当可进行自我修复。 要做到这点我们需要从以下三个方面入手:
- 检测故障。
- 从容应对故障。
- 记录和监视故障,获取操作见解。
如何应对特定类型的故障可能取决于应用程序的可用性需求。 例如,如果需要非常高的可用性,则可能在区域中断期间自动故障转移到次要区域。 然而,这将使成本高于单区域部署。
此外,不要只考虑像区域中断这类大事件,因为这种情况通常鲜有发生。 应该尽可能将注意力集中在处理本地短期的故障上,例如网络连接故障或数据库连接失败等。
建议
一、重试失败的操作
发生暂时性故障的原因可能有:短暂的网络连接中断、删除了数据库连接或服务因繁忙而超时。 在应用程序中构建重试逻辑来处理暂时性故障。 对于做好幂等操作的服务,客户端 SDK 可实施自动重试。 有关详细信息,请参阅“暂时性故障处理和重试模式”。
二、保护故障远程服务(断路器)
在暂时性故障后最好进行重试,但如果故障仍然存在,最终可能会有非常多的调用方联接故障服务。 但大并发量情况下对于所有失败的请求都进行了重试的话,这可能导致级联故障。 而使用断路器模式 ,无需在操作发生故障时重复无效的远程调用就能快速解决这个问题,并且有效保障问题服务。
三、隔离关键资源
子系统中的故障有时会发生级联。 如果某个故障导致某些资源(例如线程或套接字)无法及时释放,导致资源耗尽,则可能会发生这种连锁反应。 为了避免此问题,请将系统分区为独立的组,使一个分区中的故障不会导致整个系统瘫痪。
四、负载分级
应用程序可能会遇到突发流量高峰,导致后端上的服务瘫痪。 为了避免此问题,请使用基于队列的负载调节模式使工作项排队进行异步运行。 队列充当可平缓负载高峰的缓冲区。
五、故障转移
如果无法访问某个实例,请故障转移到另一个实例。 对于 Web 服务器之类的无状态对象,请在负载均衡器或流量管理器后放置一些实例。 对于数据库之类的存储状态的对象,请使用副本和故障转移。 根据数据存储和复制方式,可能需要应用程序处理最终的一致性。
六、补偿失败的事务
一般情况下,需避免分布式事务,因为它们需要协调服务和资源。 相反地应该用较小的单个事务组成操作。 如果在中途操作失败,请使用补偿事务撤销已完成的所有步骤。
七、使用版本或检查点功能
如果长时间运行的操作失败,检查点可以提供复原能力。 当操作重新启动时它可以从上一个检查点恢复。
八、正常降级
有时某个问题无法解决,但可以提供仍然有用的缩减版功能。 假设某个应用程序要显示商品详情。 如果该应用程序无法检索商品的缩略图图像,它可能显示占位符图像。 整个子系统可能对应用程序不重要。 例如,在电子商务网站,显示产品建议可能没有处理订单重要。
九、限制客户端
有时少量的用户会产生过多的负载,降低了应用程序对其他用户的可用性。 在这种情况下,可以在一段时间内限制客户端。 请参阅“限制模式”。
十、阻止错误执行组件
仅仅限制客户端并不意味着客户端的行为是恶意的。 它只意味着客户端超出其服务配额。 但如果客户端持续超出其配额或在其他方面具有不良行为,则可能需要进行阻止。在这种情况下我们还可以定义一个异常的例外进程,供用户请求解除阻止。
十一、使用协调器选拔或推举方式
当需要协调任务时,请使用选拔方式来选择协调器。 这样协调器不是单一故障点。 如果协调器失败,则选择一个新的协调器。 这种开源组件相当多,比如现成的解决方案 Zookeeper。
十二、使用故障注入进行测试
通常情况下正常的路径会得到精心的测试,而异常的路径却不会。 系统在生产中长时间运行后,可能会产生各种异常路径。 通过触发实际故障或模拟故障,使用故障注入来测试系统对故障的复原能力。
十三、采用混沌工程
混沌工程通过将故障或异常情况随机注入到生产实例中,扩展了故障注入的概念。