隔舱模式
隔舱模式是一种可以承受故障的应用程序设计类型。 在隔舱体系结构中,应用程序的元素被隔离到池中,因此如果一个失败,其他元素将继续运行。 它以船体的分节隔板(隔舱)命名。 如果船体受到破坏,只有受损的分段才会进水,从而可以防止船只下沉。
上下文和问题
基于云的应用程序可以包含多个服务,其中每个服务具有一个或多个使用者。 服务过载或发生故障会影响服务的所有使用者。 此外,一个使用者可以使用每个请求的资源同时向多个服务发送请求。 当使用者向配置不当或无响应的服务发送请求时,可能无法及时释放客户端请求所用的资源。 随着不断地向服务发送请求,这些资源可能会耗尽。 例如,客户端的连接池可能会耗尽。 此时,使用者向其他服务发出的请求会受到影响。 最终,使用者不再能够向其他服务(而不仅仅是原始的无响应服务)发送请求。 资源耗尽问题同样会影响具有多个使用者的服务。 源自一个客户端的大量请求可能耗尽服务中的可用资源。 其他使用者不再能够使用该服务,从而导致连锁故障效应。
解决方案
根据使用者负载和可用性要求,将服务实例分区成不同的组。 此设计有助于隔离故障,即使在发生故障期间,也能为某些使用者保留服务功能。 使用者也可以将资源分区,确保用于调用一个服务的资源不会影响用于调用另一个服务的资源。 例如,对于调用多个服务的使用者,可为其分配每个服务的连接池。 如果某个服务开始发生故障,只有分配给该服务的连接池才会受到影响,因此,使用者可继续使用其他服务。 此模式的优势包括:
- 隔离使用者和服务,防止发生连锁故障。 可在使用者或服务自身的隔舱中隔离对其造成影响的问题,防止整个解决方案发生故障。
- 在发生服务故障时,可以保留一部分功能。 应用程序的其他服务和功能可继续工作。
- 可以部署能够为使用方应用程序提供不同服务质量的服务。 可以配置高优先级使用者池来利用高优先级服务。
下图显示了围绕调用单个服务的连接池构建的隔舱。 如果服务 A 发生故障或导致其他某种问题,该连接池将被隔离,因此,只有使用分配给服务 A 的线程池的工作负荷才受影响。 使用服务 B 和 C 的工作负载不受影响,可继续工作而不会中断。 下图显示了调用单个服务的多个客户端。 为每个客户端分配了独立的服务实例。 客户端 1 发出了过多的请求,使其实例近乎瘫痪。 由于每个服务实例相互隔离,其他客户端可继续发出调用。
问题和注意事项
- 围绕应用程序的业务和技术要求定义分区。
- 将服务或使用者分区到隔舱时,请考虑相应技术提供的隔离级别,以及成本、性能和可管理性方面的开销。
- 考虑将隔舱与重试、断路器和限制模式合并,提供更周密的故障处理。
- 将使用者分区到隔舱时,请考虑使用进程、线程池和信号灯。 resilience4j 和 Polly 等项目提供了用于创建消费者隔舱的框架。
- 将服务分区到隔舱时,请考虑将这些服务部署到独立的虚拟机、容器或进程。 容器能够以相当低的开销合理平衡资源隔离。
- 使用异步消息通信的服务可以通过不同的队列集进行隔离。 每个队列可以包含专用的实例集用于处理该队列中的消息,或者包含单个实例组,以通过某种算法来取消排队和调度处理负载。
- 确定隔舱的粒度级。 例如,若要将租户分配到不同的分区,可将每个租户放入独立的分区,或者将多个租户放入一个分区。
- 监视每个分区的性能和 SLA。
何时使用此模式
使用此模式可以:
- 隔离使用一组后端服务所用的资源,尤其是应用程序可以提供某种功能级别时,即使某个服务未能响应。
- 将关键使用者与标准使用者相隔离。
- 防止应用程序发生连锁故障。
此模式可能不适用于以下情况:
- 项目中可能不接受资源的低效利用。
- 没有必要提高复杂性
示例
以下 Kubernetes 配置文件创建一个隔离的容器用于运行单个服务,该容器具有自身的 CPU 和内存资源与限制。
apiVersion: v1
kind: Pod
metadata:
name: drone-management
spec:
containers:
- name: drone-management-container
image: drone-service
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "1"