

# REL 5  您如何在分布式系统中进行交互设计，从而缓解或经受住故障影响？
<a name="w2aac19b9b7b9"></a>

分布式系统依赖于通信网络以便使组件互相连接（如服务器或服务等）。尽管这些网络中存在数据丢失或延迟，但是您的工作负载必须可靠运行。分布式系统组件的运行方式不得对其他组件或工作负载产生负面影响。这些最佳实践使工作负载能够承受压力或故障，从中更快地恢复，并且降低此类伤害的影响。其结果是缩短平均恢复时间（MTTR）。

**Topics**
+ [REL05-BP01 实施轻松降级以将适用的硬依赖关系转换为软依赖关系](rel_mitigate_interaction_failure_graceful_degradation.md)
+ [REL05-BP02 限制请求](rel_mitigate_interaction_failure_throttle_requests.md)
+ [REL05-BP03 控制与限制重试调用](rel_mitigate_interaction_failure_limit_retries.md)
+ [REL05-BP04 快速失效机制和限制队列](rel_mitigate_interaction_failure_fail_fast.md)
+ [REL05-BP05 设置客户端超时](rel_mitigate_interaction_failure_client_timeouts.md)
+ [REL05-BP06 尽可能使服务为无状态](rel_mitigate_interaction_failure_stateless.md)
+ [REL05-BP07 实施紧急杠杆](rel_mitigate_interaction_failure_emergency_levers.md)

# REL05-BP01 实施轻松降级以将适用的硬依赖关系转换为软依赖关系
<a name="rel_mitigate_interaction_failure_graceful_degradation"></a>

 某个组件的依赖关系运行不正常时，该组件仍可在性能降低的条件下运行。例如，当依赖关系调用失败时，进行故障转移，使用预先确定的静态响应。 

 假设被服务 A 调用的服务 B 反过来调用服务 C。 

![\[图中显示若服务 B 调用服务 C 失败，服务 B 向服务 A 返回降级响应\]](http://docs.aws.amazon.com/zh_cn/wellarchitected/2022-03-31/framework/images/graceful-degradation.png)


 当服务 B 调用服务 C 时，它会收到错误或超时消息。而服务 B，因为缺少来自服务 C（及其所包含数据）的响应，则会返回它能够做出的响应。它可以是最后缓存的正确值，或服务 B 可以使用预先确定的静态响应取代它收到的来自服务 C 的响应。然后，向调用方（即服务 A）返回降级响应。若无此静态响应，服务 C 的故障将级联传递至服务 B 和服务 A，因此而丧失可用性。 

 按照硬依赖关系可用性等式中的倍乘系数（见 [https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/availability.html#dbedbedda68f9a15ACLX122](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/availability.html#dbedbedda68f9a15ACLX122)），C 的可用性的任何降低将严重影响 B 的有效可用性。通过返回静态响应，服务 B 能够缓解 C 中的故障的影响，而且，虽然被降级，可使服务 C 看起来似乎 100% 可用（假设它在错误的情况下可靠地返回静态响应）。注意，静态响应是返回错误的简单替代，而不是使用其他方式对响应进行重新计算的尝试。此类采用完全不同的机制试图达到相同结果的尝试被称作回退行为，是一种要被避免的反模式。 

 优雅降级的另一个例子是 *断路器模式*.当故障为临时性时，应采用重试策略。若情况并非如此，而且操作很有可能失败，则断路器模式会防止客户端执行可能失败的请求。系统照常处理请求时，断路器会处于关闭状态，让请求正常通过。当远程系统开始返回错误或出现高延迟，断路器就会打开，依赖项被忽略，或者结果会被更轻松获取但较不全面的响应（可能只是响应缓存）所取代。系统将定期尝试调用依赖项，以确定它是否已恢复。出现这种情况时，断路器将处于关闭状态。 

![\[显示断路器处于开启和关闭状态的图表。\]](http://docs.aws.amazon.com/zh_cn/wellarchitected/2022-03-31/framework/images/circuit-breaker.png)


 除了图表中显示的关闭和开启状态，在开启状态内的可配置时间段以后，断路器可能会变为半开启状态。在此状态中，它会以远低于正常水平的速率定期尝试调用服务。此探测器用于检查服务的运行状况。在半开启状态中多次成功以后，断路器会转为关闭，并恢复正常请求。 

 **未建立这种最佳实践的情况下暴露的风险等级：** 高 

## 实施指导
<a name="implementation-guidance"></a>
+  实施轻松降级以将适用的硬依赖关系转换为软依赖关系。某个组件的依赖关系运行不正常时，该组件仍可在性能降低的条件下运行。例如，当依赖关系调用失败时，进行故障转移，使用预先确定的静态响应。 
  +  通过返回静态响应，您的工作负载会缓解其依赖项中发生的故障。
    +  [Well-Architected 实验室：第 300 级：实施运行状况检查和管理依赖项以提高可靠性](https://wellarchitectedlabs.com/Reliability/300_Health_Checks_and_Dependencies/README.html) 
  +  在重试操作可能失败时检测到该情况，并防止您的客户端使用断路器模式进行失败调用。
    +  [CircuitBreaker](https://martinfowler.com/bliki/CircuitBreaker.html) 

## 资源
<a name="resources"></a>

 **相关文档：** 
+  [Amazon API Gateway：对 API 请求限流以提高吞吐量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [CircuitBreaker（对《发布它！》一书中的“断路器”部分进行的总结）](https://martinfowler.com/bliki/CircuitBreaker.html) 
+  [AWS 中的错误重试和指数回退](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Michael Nygard《发布它！ 设计和部署生产就绪的软件》（Release It\$1 Design and Deploy Production-Ready Software）](https://pragprog.com/titles/mnee2/release-it-second-edition/) 
+  [Amazon Builders' Library：避免在分布式系统中回退](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免无法克服的队列积压](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：缓存挑战和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 
+  [Amazon Builders' Library：为超时、重试和回退引入抖动](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相关视频：** 
+  [重试、回退和抖动：AWS re:Invent 2019：介绍 Amazon Builders’ Library（DOP328）](https://youtu.be/sKRdemSirDM?t=1884) 

 **相关示例：** 
+  [Well-Architected 实验室：第 300 级：实施运行状况检查和管理依赖项以提高可靠性](https://wellarchitectedlabs.com/Reliability/300_Health_Checks_and_Dependencies/README.html) 

# REL05-BP02 限制请求
<a name="rel_mitigate_interaction_failure_throttle_requests"></a>

 限制请求是对需求意外增加做出响应的缓解模式。部分请求会得到执行，但超出定义限制的请求会被拒绝，并返回说明它们已被限制的消息。客户端预期将会回退，并且放弃请求或以较低速率进行重试。 

 您的服务应设计为可以应对每个节点或单元格所能处理的已知请求容量。此容量可以通过负载测试确定。然后，您需要跟踪请求到达速率，如果临时到达速率超过此限制，则相应的响应是发出信号表明请求已被限制。这允许用户进行重试，或许会向可能具有可用容量的另一个节点或单元格发出请求。Amazon API Gateway 提供一些限制请求的方法。Amazon SQS 和 Amazon Kinesis 可对请求进行缓冲，平滑请求速率并降低对可异步处理的请求进行限制的需求。 

 **未建立此最佳实践暴露的风险等级：** 高 

## 实施指导
<a name="implementation-guidance"></a>
+  限制请求。这是对按需求意外增加做出响应的缓解模式。部分请求会得到执行，但超出定义限制的请求会被拒绝，并返回说明它们已被限制的消息。客户端预期将会回退，并且放弃请求或以较低速率进行重试。 
  +  使用 Amazon API Gateway 
    +  [对 API 请求限流以提高吞吐量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 

## 资源
<a name="resources"></a>

 **相关文档：** 
+  [Amazon API Gateway：对 API 请求限流以提高吞吐量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [AWS 中的错误重试和指数回退](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Amazon Builders' Library：避免在分布式系统中回退](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免无法克服的队列积压](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：为超时、重试和回退引入抖动](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 
+  [对 API 请求限流以提高吞吐量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 

 **相关视频：** 
+  [重试、回退和抖动：AWS re:Invent 2019：介绍 Amazon Builders’ Library（DOP328）](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP03 控制与限制重试调用
<a name="rel_mitigate_interaction_failure_limit_retries"></a>

 在逐渐延长的间隔以后使用指数回退进行重试。引入抖动使此类重试间隔随机化，并限制重试的最大次数。 

 分布式软件系统中的常见组件包括服务器、负载均衡器、数据库和 DNS 服务器。在操作中，受故障影响，任何此类组件都可能开始生成错误。处理错误的默认方式为，在客户端实施重试。此方法可提高应用程序的可靠性和可用性。不过，如果规模较大，而且客户端在错误发生时立即重试失败的操作，网络中的新请求和重试请求可能会快速导致饱和，并争用网络带宽。这可能导致 *重试风暴，* 从而降低服务的可用性。此模式可能会继续，直到发生全系统故障。 

 为避免出现此情况，应使用回退算法，如常用的 *指数回退* 。指数回退算法会逐渐降低执行重试的速率，从而避免网络阻塞。 

 很多开发工具包和软件库（包括 AWS 的开发工具包和软件库）都实施此类算法的一种版本。但是， **别心存侥幸地认为已采用回退算法，始终要进行测试和验证。** 

 仅依靠回退还不够，因为在分布式系统中，所有客户端都可能同步回退，形成重试调用集群。Marc Brooker 在他的博文 [指数回退和抖动](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-italics%0djitter/)中解释了如何修改指数回退中的 wait() 函数以防止形成重试调用集群。他给出的解决办法 *是* 在 wait() 函数中增加抖动。要避免过长时间的重试，实施应为回退设置一个最大值限制。 

 最后，务必要配置 *最大重试次数* 或已用时间，在此以后，重试将失败。AWS 开发工具包将默认实施此操作，而且也可以对它进行配置。针对堆栈中较低的服务，最大重试限制为 0 或 1 可以限制风险，而且在将重试委派给堆栈较高的服务时依然有效。 

 **未建立这种最佳实践的情况下暴露的风险等级：** 高 

## 实施指导
<a name="implementation-guidance"></a>
+  控制与限制重试调用。在逐渐延长的间隔以后使用指数回退进行重试。引入抖动使此类重试间隔随机化，并限制重试的最大次数。 
  +  [AWS 中的错误重试和指数回退](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
    + 默认情况下，Amazon SDK 实施重试和指数回退。在调用自己的依赖服务时，您需要在依赖关系层中执行类似的逻辑。根据您的使用案例确定超时以及何时停止重试。

## 资源
<a name="resources"></a>

 **相关文档：** 
+  [Amazon API Gateway：对 API 请求限流以提高吞吐量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [AWS 中的错误重试和指数回退](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Amazon Builders' Library：避免在分布式系统中回退](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免无法克服的队列积压](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：缓存挑战和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 
+  [Amazon Builders' Library：为超时、重试和回退引入抖动](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相关视频：** 
+  [重试、回退和抖动：AWS re:Invent 2019：介绍 Amazon Builders’ Library（DOP328）](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP04 快速失效机制和限制队列
<a name="rel_mitigate_interaction_failure_fail_fast"></a>

 如果工作负载无法成功响应请求，则快速试错。这样可释放与请求关联的资源，并允许该服务在资源不足的情况下恢复。如果工作负载能够成功响应，但请求速率过高，则使用队列来对请求进行缓冲。不过，不要允许使用长队列，它可能导致处理已被客户端放弃的过时请求。 

 此最佳实践适用于请求的服务器端，或接收方。 

 请注意，可在系统的多个级别创建队列，它们可能严重妨碍快速恢复的能力，因为需要先处理较旧的过时请求（虽然不再需要响应），然后才会处理较新的请求。另外还要注意队列所在的位置。它们通常会隐藏在工作流或记录到数据库的工作中。 

 **未建立此最佳实践暴露的风险等级：** 高 

## 实施指导
<a name="implementation-guidance"></a>
+  快速失效机制和限制队列。如果工作负载无法成功响应请求，则快速试错。这样可释放与请求关联的资源，并允许该服务在资源不足的情况下恢复。如果工作负载能够成功响应，但请求速率过高，则使用队列来对请求进行缓冲。不过，不要允许使用长队列，它可能导致处理已被客户端放弃的过时请求。 
  +  在服务面临压力时执行快速失效机制。
    +  [快速试错](https://www.martinfowler.com/ieeeSoftware/failFast.pdf) 
  +  限制队列：在基于队列的系统中，如果在停止处理后消息仍不断涌入，则消息债务可能造成大量积压，从而增加处理时间。工作完成太晚，以至于结果无法发挥作用，从根本上导致了队列原本要避免的可用性打击问题。
    +  [Amazon Builders' Library：避免无法克服的队列积压](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 

## 资源
<a name="resources"></a>

 **相关文档：** 
+  [AWS 中的错误重试和指数回退](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [快速试错](https://www.martinfowler.com/ieeeSoftware/failFast.pdf) 
+  [Amazon Builders' Library：避免在分布式系统中回退](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免无法克服的队列积压](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：缓存挑战和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 
+  [Amazon Builders' Library：为超时、重试和回退引入抖动](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相关视频：** 
+  [重试、回退和抖动：AWS re:Invent 2019：介绍 Amazon Builders’ Library（DOP328）](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP05 设置客户端超时
<a name="rel_mitigate_interaction_failure_client_timeouts"></a>

 适当设置超时，对它们进行系统性验证，而且不要依靠默认值，因为默认值通常设置得过高。 

 此最佳实践适用于请求的客户端，或发送方。 

 为任何远程调用和大体上任何跨流程调用设置连接超时和请求超时。许多框架具有内置超时功能，但仍需谨慎，因为许多默认值为无限值或过高。过高的值会降低超时的实用性，因为客户端等待超时发生时，系统会继续消耗资源。过低的值可能因为要重试过多请求而导致后端流量增加以及延迟变长。在有些情况下，由于要对全部请求进行重试，从而可能导致完全中断。 

 要了解关于 Amazon 如何利用超时、重试和抖动回退的更多信息，请参阅 [https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/?did=ba_card&trk=ba_card](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/?did=ba_card&trk=ba_card). 

 **未建立这种最佳实践的情况下暴露的风险等级：** 高 

## 实施指导
<a name="implementation-guidance"></a>
+  为任何远程调用和大体上任何跨流程调用设置连接超时和请求超时。许多框架具有内置超时功能，但仍需谨慎，因为许多默认值为无限值或过高。过高的值会降低超时的实用性，因为客户端等待超时发生时，系统会继续消耗资源。过低的值可能因为要重试过多请求而导致后端流量增加以及延迟变长。在有些情况下，由于要对全部请求进行重试，从而可能导致完全中断。 
  +  [AWS 开发工具包：重试次数和超时](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/retries-timeouts.html) 

## 资源
<a name="resources"></a>

 **相关文档：** 
+  [AWS 开发工具包：重试次数和超时](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/retries-timeouts.html) 
+  [Amazon API Gateway：对 API 请求限流以提高吞吐量](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html) 
+  [AWS 中的错误重试和指数回退](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) 
+  [Amazon Builders' Library：为超时、重试和回退引入抖动](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) 

 **相关视频：** 
+  [重试、回退和抖动：AWS re:Invent 2019：介绍 Amazon Builders’ Library（DOP328）](https://youtu.be/sKRdemSirDM?t=1884) 

# REL05-BP06 尽可能使服务为无状态
<a name="rel_mitigate_interaction_failure_stateless"></a>

 服务应该不需要状态，或者在不同的客户端请求之间卸载状态，磁盘上和内存中本地存储的数据不存在依赖关系。这使任意替换服务器成为可能，而且不会对可用性产生影响。Amazon ElastiCache 或 Amazon DynamoDB 是卸载状态的理想目标位置。 

![\[在此无状态 Web 应用程序中，会话状态会被卸载到 Amazon ElastiCache。\]](http://docs.aws.amazon.com/zh_cn/wellarchitected/2022-03-31/framework/images/stateless-webapp.png)


 当用户或服务与应用程序进行交互，它们通常会执行一系列交互并构成一次会话。对于用户来说，会话是他们在使用应用程序时持续存在于请求之间的特殊数据。无状态应用程序是无需掌握之前交互而且不会存储会话信息的应用程序。 

 若采用无状态设计，则您可以使用无服务器计算服务，如 AWS Lambda 或 AWS Fargate。 

 除了服务器替换，无状态应用程序的另一项优点是，由于任何可用的计算资源（如 EC2 实例和 AWS Lambda 函数）都可以处理任何请求，因此它们可以进行横向扩展。 

 **未建立此最佳实践暴露的风险等级：** 中 

## 实施指导
<a name="implementation-guidance"></a>
+  让应用程序无状态。无状态应用程序支持水平扩展，并且可以承受单个节点故障。 
  +  删除可能存储在请求参数中的状态。
  +  在检查是否需要状态之后，将任何状态追踪移动到具有弹性的多区域缓存或数据存储（如 Amazon ElastiCache、Amazon RDS、Amazon DynamoDB 或第三方分布式数据解决方案）。存储无法移动到弹性数据存储的状态。
    +  某些数据（例如 cookie）可能在标头或查询参数中传递。 
    +  进行重构，从而删除可能在请求中快速传递的状态。
    +  提交请求时实际上并不需要某些数据，这些数据可以按需检索。
    +  删除可以异步检索的数据。
    +  确定满足所需状态要求的数据存储。 
    +  考虑针对非关系型数据使用 NoSQL 数据库。

## 资源
<a name="resources"></a>

 **相关文档：** 
+  [Amazon Builders' Library：避免在分布式系统中回退](https://aws.amazon.com/builders-library/avoiding-fallback-in-distributed-systems) 
+  [Amazon Builders' Library：避免无法克服的队列积压](https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs) 
+  [Amazon Builders' Library：缓存挑战和策略](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/) 

# REL05-BP07 实施紧急杠杆
<a name="rel_mitigate_interaction_failure_emergency_levers"></a>

 紧急杠杆是可帮助您的工作负载减轻可用性影响的快速流程。 

 **未建立这种最佳实践的情况下暴露的风险等级：** 中 

## 实施指导
<a name="implementation-guidance"></a>
+  实施紧急杠杆。这些是可帮助您的工作负载减轻可用性影响的快速流程。即使未找到根本原因，它们也可以运行。理想的紧急杠杆可通过提供完全确定的激活与停用标准，将解析器的认知负担降低到零。杠杆通常需要手动操作，但也可实现自动化 
  +  杠杆示例包括， 
    +  阻止所有机器人流量 
    +  为静态页面而非动态页面提供服务 
    +  减少对依赖项的调用频率 
    +  限制来自依赖项的调用 
  +  关于实施和使用紧急杠杆的提示 
    +  当杠杆被激活时，求少不求多 
    +  保持简单，避免双模态行为 
    +  定期测试您的杠杆 
  +  以下为非紧急杠杆的操作示例 
    +  添加容量 
    +  号召依赖您的服务的客户端服务所有者，要求他们降低调用 
    +  更改代码并将其释放 