Azure 设计模式之船舱模式
船舱模式
将应用程序中的元素放到隔离的池中,以便如果一个失败,其他的将继续运行。
之所以这种模式被命名为“舱壁”,因为将其类比于对船体的剖面划分。如果一个船体受到损害,只有损坏的部分才会充满水,从而防止整个船舶沉没。
问题背景
云应用可以包括多个服务,每个服务具有一个或多个消费者。服务中的负载过重或故障将影响该服务的所有消费者。
此外,消费者可以使用同一个请求资源同时向多个服务发送请求。当消费者向配置错误或未响应的服务发送请求时,客户端请求所使用的资源可能不会及时得到释放。随着更多请求的到达,这些资源可能会被耗尽。例如,客户端的连接池可能已耗尽。此时,消费者对其他服务的请求也将受到影响。最终,消费者不能再向任何服务发送请求,而不仅仅是原始的无响应的服务。
同样的,资源耗尽问题会影响到多个消费者的服务。来自一个客户端的大量请求可能会耗尽服务中的可用资源。而其他消费者不再能够使用该服务,导致级联故障。
解决方案
根据消费者的负载和可用性要求将服务实例划分成不同的组。此设计有助于故障隔离,并允许为某些消费者保持服务,即使在故障期间。
消费者可以对资源进行分区,以确保用于调用一个服务的资源不会影响用于调用另一个服务的资源。例如,调用多个服务的消费者可以为每个服务分配一个连接池。如果调用失败,它只会影响为该服务分配的连接池,从而允许消费者继续使用其他服务。
这种模式的好处包括:
将消费者和服务从级联故障隔离。影响消费者或服务的问题可以在自己的舱壁内隔离开,从而防止整个解决方案发生故障。
在发生服务故障时只影响部分功能。其他服务和功能将继续工作。
能够为消费者应用程序部署不同质量级别的服务。高优先级消费者对应于使用高优先级服务。
下图显示了为服务构建调用连接池的隔板。如果服务A失败或导致某些其他问题,连接池是隔离的,因此只有使用分配给服务A的线程池的工作负载才会受到影响。使用服务B和C的工作负载不受影响,可以继续工作而不中断。
下图显示了多个客户端调用单个服务的场景。每个客户端被分配一个单独的服务实例。客户端1发送了太多请求并压垮了它的实例。由于每个服务实例与其他实例是隔离的,其他客户端可以继续调用。
问题和思考
围绕应用程序的业务和技术需求来定义分区。
当对服务或消费者划分隔板时,要考虑技术上提供的隔离级别以及成本,性能和维护方面的开销。
考虑将舱壁与重试,断路器和节流模式相结合,以提供更复杂的故障处理方案。
对消费者划分隔板时,考虑使用进程,线程池和信号量。 Netflix Hystrix(https://github.com/Netflix/Hystrix)和Polly(https://github.com/App-vNext/Polly)等项目提供了创建消费者舱壁的框架。
将服务分成隔板时,考虑将其部署到单独的虚拟机,容器或进程中。容器很好的平衡了资源隔离产生的开销。
使用异步消息进行通信的服务可以使用不同的队列进行隔离。每个队列可以包含处理队列消息的一组实例,或者是使用算法进行出队和分发处理的一组实例。
确定舱壁的粒度级别。例如,如果要跨越分区分配租户,可以将每个租户分成一个单独的分区,或将多个租户放入一个分区。
监视每个分区的性能和SLA。
何时使用这种模式
隔离用于消耗一组后台服务的资源,特别是如果应用程序仍可以提供某种级别的功能,即使在其中某服务没有响应的情况下。
隔离标准消费者中的关键消费者。
保护应用免受级联故障的影响。
对于以下情况,此模式可能不适用:
资源使用率不高是无法接受的。
隔离所增加的复杂性是不必要的。
例子
以下Kubernetes配置文件创建一个独立的容器来运行单个服务,具有自己的CPU和内存资源和限制。
将应用程序中的元素放到隔离的池中,以便如果一个失败,其他的将继续运行。
之所以这种模式被命名为“舱壁”,因为将其类比于对船体的剖面划分。如果一个船体受到损害,只有损坏的部分才会充满水,从而防止整个船舶沉没。
问题背景
云应用可以包括多个服务,每个服务具有一个或多个消费者。服务中的负载过重或故障将影响该服务的所有消费者。
此外,消费者可以使用同一个请求资源同时向多个服务发送请求。当消费者向配置错误或未响应的服务发送请求时,客户端请求所使用的资源可能不会及时得到释放。随着更多请求的到达,这些资源可能会被耗尽。例如,客户端的连接池可能已耗尽。此时,消费者对其他服务的请求也将受到影响。最终,消费者不能再向任何服务发送请求,而不仅仅是原始的无响应的服务。
同样的,资源耗尽问题会影响到多个消费者的服务。来自一个客户端的大量请求可能会耗尽服务中的可用资源。而其他消费者不再能够使用该服务,导致级联故障。
解决方案
根据消费者的负载和可用性要求将服务实例划分成不同的组。此设计有助于故障隔离,并允许为某些消费者保持服务,即使在故障期间。
消费者可以对资源进行分区,以确保用于调用一个服务的资源不会影响用于调用另一个服务的资源。例如,调用多个服务的消费者可以为每个服务分配一个连接池。如果调用失败,它只会影响为该服务分配的连接池,从而允许消费者继续使用其他服务。
这种模式的好处包括:
将消费者和服务从级联故障隔离。影响消费者或服务的问题可以在自己的舱壁内隔离开,从而防止整个解决方案发生故障。
在发生服务故障时只影响部分功能。其他服务和功能将继续工作。
能够为消费者应用程序部署不同质量级别的服务。高优先级消费者对应于使用高优先级服务。
下图显示了为服务构建调用连接池的隔板。如果服务A失败或导致某些其他问题,连接池是隔离的,因此只有使用分配给服务A的线程池的工作负载才会受到影响。使用服务B和C的工作负载不受影响,可以继续工作而不中断。
下图显示了多个客户端调用单个服务的场景。每个客户端被分配一个单独的服务实例。客户端1发送了太多请求并压垮了它的实例。由于每个服务实例与其他实例是隔离的,其他客户端可以继续调用。
问题和思考
围绕应用程序的业务和技术需求来定义分区。
当对服务或消费者划分隔板时,要考虑技术上提供的隔离级别以及成本,性能和维护方面的开销。
考虑将舱壁与重试,断路器和节流模式相结合,以提供更复杂的故障处理方案。
对消费者划分隔板时,考虑使用进程,线程池和信号量。 Netflix Hystrix(https://github.com/Netflix/Hystrix)和Polly(https://github.com/App-vNext/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"
···