本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
断路器模式
意图
断路器模式可以防止呼叫者服务在呼叫之前导致重复超时或失败时重试对另一个服务(被叫者)的呼叫。该模式还用于检测被叫服务何时恢复运行。
动机
当多个微服务协作处理请求时,一个或多个服务可能会变得不可用或出现高延迟。当复杂的应用程序使用微服务时,一个微服务中断可能会导致应用程序故障。微服务通过远程过程调用进行通信,网络连接中可能会出现瞬态错误,从而导致故障。(暂时性错误可以通过使用带退避的重试模式来处理。) 在同步执行期间,超时或故障的层叠可能会导致用户体验不佳。
但是,在某些情况下,故障可能需要更长的时间才能解决,例如,当被调用者服务关闭或数据库争用导致超时时。在这种情况下,如果调用服务重复重试调用,则这些重试可能会导致网络争用和数据库线程池消耗。此外,如果多个用户反复重试应用程序,则会使问题变得更糟,并可能导致整个应用程序的性能下降。
迈克尔·尼加德在他的《Rele ase It》(Nygard 2018)一书中推广了断路器模式。这种设计模式可以防止呼叫者服务重试先前导致重复超时或失败的服务呼叫。它还可以检测被叫服务何时恢复运行。
断路器物体的工作原理类似于电路断路器,当电路出现异常时,断路器会自动中断电流。出现故障时,电气断路器会关闭或跳闸电流。同样,断路器对象位于呼叫方和被叫方服务之间,如果被叫方不可用,则断路器会跳闸。
分布式计算的谬
在网络中断期间,应用程序可能会无限期地等待回复并持续消耗应用程序资源。网络可用时未能重试这些操作也可能导致应用程序性能下降。如果由于网络问题而导致对数据库或外部服务的API调用超时,则在没有断路器的情况下重复调用可能会影响成本和性能。
适用性
在以下情况下使用此模式:
-
呼叫者服务拨打的呼叫很可能失败。
-
被调用方服务表现出的高延迟(例如,当数据库连接速度较慢时)会导致被调用服务超时。
-
呼叫者服务进行同步呼叫,但被叫方服务不可用或延迟很长。
问题和注意事项
-
与@@ 服务无关的实现:为防止代码膨胀,我们建议您以与微服务无关且由微服务驱动的方式实现断路器对象。API
-
被叫方关闭电路:当被叫方从性能问题或故障中恢复过来时,他们可以将电路状态更新为。
CLOSED
这是断路器模式的扩展,如果您的恢复时间目标 (RTO) 需要它,则可以实施。 -
多线程呼叫:过期超时值定义为在再次路由呼叫以检查服务可用性之前,电路保持跳闸的时间段。在多个线程中调用被调用者服务时,第一个失败的调用定义了过期超时值。您的实现应确保后续调用不会无休止地移动到期超时。
-
强制打开或关闭电路:系统管理员应该能够打开或关闭电路。这可以通过更新数据库表中的过期超时值来完成。
-
可观察性:应用程序应设置日志记录,以识别断路器打开时失败的呼叫。
实施
高级架构
在以下示例中,来电者是订单服务,被叫方是支付服务。
如果没有故障,订单服务会通过断路器将所有呼叫路由到支付服务,如下图所示。
如果支付服务超时,断路器可以检测到超时并跟踪故障。
如果超时超过指定阈值,则应用程序将打开电路。当电路开启时,断路器对象不会将呼叫路由到支付服务。当订单服务调用支付服务时,它会立即返回失败。
断路器对象会定期尝试查看对支付服务的调用是否成功。
成功呼叫支付服务后,电路将关闭,所有后续呼叫将再次路由到支付服务。
使用 AWS 服务实施
示例解决方案使用中的快速工作流程AWS Step Functions
该解决方案还使用 A mazon Dyn
当服务想要调用其他服务时,它会使用被调用服务的名称启动工作流程。该工作流从 DynamoDB CircuitStatus
表中获取断路器状态,该表存储了当前已降级的服务。如果CircuitStatus
包含被叫者的未过期记录,则电路处于开启状态。Step Functions 工作流程立即返回失败并以FAIL
状态退出。
如果该CircuitStatus
表不包含被调用者的记录或包含过期的记录,则该服务可以运行。状态机定义中的ExecuteLambda
步骤调用通过参数值发送的 Lambda 函数。如果调用成功,Step Functions 工作流程将以状态退出。SUCCESS
如果服务调用失败或出现超时,应用程序将在规定的次数内以指数退避方式重试。如果服务调用在重试后失败,则工作流会在CircuitStatus
表中插入带有 a 的服务的记录ExpiryTimeStamp
,然后工作流程以状态退出。FAIL
只要断路器处于开启状态,随后对同一服务的调用就会立即返回故障。状态机定义中的Get Circuit Status
步骤根据ExpiryTimeStamp
值检查服务可用性。使用 DynamoDB 存TTL活时间 () 功能将过期项目从CircuitStatus
表中删除。
代码示例
以下代码使用 GetCircuitStatus
Lambda 函数检查断路器状态。
var serviceDetails = _dbContext.QueryAsync<CircuitBreaker>(serviceName, QueryOperator.GreaterThan, new List<object> {currentTimeStamp}).GetRemainingAsync(); if (serviceDetails.Result.Count > 0) { functionData.CircuitStatus = serviceDetails.Result[0].CircuitStatus; } else { functionData.CircuitStatus = ""; }
以下代码显示了 Step Functions 工作流程中的亚马逊州语言语句。
"Is Circuit Closed": { "Type": "Choice", "Choices": [ { "Variable": "$.CircuitStatus", "StringEquals": "OPEN", "Next": "Circuit Open" }, { "Variable": "$.CircuitStatus", "StringEquals": "", "Next": "Execute Lambda" } ] }, "Circuit Open": { "Type": "Fail" }
GitHub 存储库
有关此模式示例架构的完整实现,请参见 GitHub 存储库,网址为https://github.com/aws-samples/circuit-breaker-netcore-blog