失败是不可避免的。然而,正确的软件设计和开发选择可以帮助最大限度地减少其影响、隔离问题并加快恢复时间。
许多架构师努力设计具有避免灾难性故障的能力的应用程序系统。不幸的是,在现实世界中,导致崩溃的错误和过载是不可避免的。
为了正确处理此类故障,开发团队必须为自己配备正确的软件弹性实践。在追求设计风格(例如基于微服务的架构)时,这一点尤为重要,在这种架构中,故障可能会蔓延到分布式组件并导致广泛的中断。
各种软件弹性技术和机制可以帮助团队响应错误、启动恢复过程并在发生故障时保持一致的应用程序性能。让我们来看看架构师可以实施的四种策略来解决错误、最大限度地减少故障的影响并持续维护弹性软件架构。
创建死信队列
个人通信可能因多种原因而陷入困境,例如不可用的收件人、格式不正确的请求和数据丢失。这在事件驱动架构中尤其成问题,其中请求通常被放入消息队列以等待处理,而请求服务继续进行下一个操作。因此,未处理的消息会迅速堵塞这些队列。
死信队列引入了一种机制,专注于处理这些流浪消息,以防止它们使通信渠道混乱,并不必要地吸走保持它们流通所需的资源。开发团队可以设置死信队列来识别滞留消息并隔离故障。这使架构师能够检查特定错误,并维护有助于指导未来设计选择的详细历史文档。
一旦这些恶意消息被认为是过时的,就可以随意从队列中删除它们。或者,可以将它们重新提交以恢复操作或复制错误以进行调试。
使用功能切换进行修改
软件弹性的另一个重要因素与开发团队的功能更新发布周期的方法有关。与其停止添加功能和修改应用程序功能的操作,组织可以使用功能切换方法在推出和更新期间保持应用程序正常运行。
功能切换使开发人员能够增量修改应用程序,同时保持现有生产级代码不变。金丝雀发布和 A/B 测试等技术使开发人员能够在有限数量的实例中推出更新的代码,同时将原始代码保留在生产环境中。
使用功能切换方法,团队可以通过监视新版本实例并使用类似切换机制的回滚来战略性地配置版本,以防修改导致损坏。在某些情况下,如果系统检测到某些错误或性能不一致,团队可能能够自动触发这些回滚切换。
基本弹性设计模式
为了维护弹性软件,开发团队使用特定的设计模式,专注于包含故障和提供紧急对策。许多模式提供了这些类型的恢复机制,并阻止错误从一个分布式组件不受控制地传播到另一个分布式组件。这里有些例子:
- 舱壁(Bulkhead)。此模式隔离子系统并配置单个模块以在出现故障时停止与其他组件的通信,从而降低问题传播的风险。
- 背压(Backpressure)。背压模式自动推回超过预设流量吞吐量容量限制的工作负载请求,保护敏感系统免受过载。
- 断路器(Circuit breaker)。基于隔板和背压模式,断路器提供了一种机制,可以自动切断与有问题的组件的连接。它将定期重试连接以查看错误是否已解决。
- 批量到流(Batch-to-stream.)。该模式旨在管理批处理吞吐量,修改批处理工作负载并将其转换为简化的 OLTP 事务。
- 优雅的退化(Graceful degradation)。这种设计模式本质上为应用程序的所有主要组件安装了一个回退机制。虽然这主要是为了帮助为更新提供回滚,但它也可以在突然失败的情况下派上用场。
促进组件之间的松散耦合
传统的单体应用程序意味着紧密耦合架构中的刚性依赖关系。结果,一个软件组件几乎肯定会影响另一个。或者,在微服务等分布式系统中,架构师可以通过解耦软件组件来最小化这些依赖关系。
在松散耦合架构中,应用程序组件、模块和服务之间存在的依赖关系保持在最低限度。相反,抽象处理必要的数据传输和消息传递过程。因此,发生在一个组件上的更新或故障不太可能导致对另一个组件的意外更改。解耦可以隔离问题并防止它们在其他软件环境中传播,从而限制出现广泛错误的风险。
使用 sidecar 容器来限制故障
Sidecar 是一个支持容器,它与主应用程序容器在同一个 pod 中运行。Sidecar 使团队能够向容器添加功能并与外部服务集成,而无需更改主要的现有应用程序容器实例。
对于软件弹性,这种技术是有益的,因为主要的应用程序逻辑和代码库保持隔离,限制了风险和故障。然而,边车也有缺点。例如,添加 sidecar 意味着开发人员负责管理更多的容器和增加的资源消耗。努力确保sidecars 不会使工作负载复杂到影响应用程序性能的程度。对于初学者,您需要建立一个完整的容器监控系统,该系统将跟踪边车并衡量它们对它们所服务的生产级容器的影响。