Polly 断路器/重试以在网络中断后自动重新启动查询
Posted
技术标签:
【中文标题】Polly 断路器/重试以在网络中断后自动重新启动查询【英文标题】:Polly Circuit Breaker / Retry to automatically restart queries after a network outage 【发布时间】:2019-04-13 10:51:27 【问题描述】:我正在 .NET 框架 4.5.2 中通过 Polly 实现断路器和重试模式。
我想看看我的理解是否正确。
问题1:如果网络中断,并且断路器已经达到异常AllowedBeforeBreaking数量,进入打开状态并等待durationOfBreak期间,电路将打开新请求但已发送的请求将抛出例外?
问题 2:如果期望的行为是针对那些具有要重试异常的请求,而不是断路器抛出异常,那么除了断路器策略之外,还需要实施重试策略。我对此的理解是,问题1的行为会发生,然后会尝试重试。
A.如果出现网络中断或服务关闭,并且期望的行为是在网络恢复或服务再次启动时重试请求,则需要执行 RetryForever。有没有更好的方法来做到这一点?实际上会有很多阻塞,对吗?
在代码方面,我的策略目前定义为:
const int maxRetryAttempts = 3;
const int exceptionsAllowedBeforeBreaking = 2;
const int pauseBetweenFailures = 2;
readonly Policy retryPolicy = Policy
.Handle<Exception>()
.RetryAsync(maxRetryAttempts, (exception, retryCount) => System.Diagnostics.Debug.WriteLine($"Retry retryCount"));
readonly Policy circuitBreakerPolicy = Policy
.Handle<Exception>()
.CircuitBreakerAsync(exceptionsAllowedBeforeBreaking: exceptionsAllowedBeforeBreaking,
durationOfBreak: TimeSpan.FromSeconds(pauseBetweenFailures),
onBreak: (e, span) => System.Diagnostics.Debug.WriteLine("Breaking circuit for " + span.TotalMilliseconds + "ms due to " + e.Message),
onReset: () => System.Diagnostics.Debug.WriteLine("Trial call succeeded: circuit closing again."),
onHalfOpen: () => System.Diagnostics.Debug.WriteLine("Circuit break time elapsed. Circuit now half open: permitting a trial call."));
我的调用代码是这样的:
var response = await retryPolicy.WrapAsync(circuitBreakerPolicy).ExecuteAsync(() => this.client.SendAsync<TData, JObject>(message, cancel, jsonSerializer));
我观察到,如果我在断路器上运行所有重试所需的时间后断开网络连接,则 CancellationToken 将设置为取消,此时所有请求都会失败。如果在此之前网络已恢复,则重试请求。
【问题讨论】:
【参考方案1】:问题1:如果网络中断并且断路器已经达到exceptionsAllowedBeforeBreaking数字,进入打开状态并等待durationOfBreak期间,电路将打开新请求......
durationOfBreak 过后,电路将转换到半开状态,在此期间a single trial call is permitted (in the current implementation)。
...但是已经发送的会抛出异常吗?
电话是placed during the Open state will throw BrokenCircuitException
。
问题 2:如果期望的行为是针对那些具有要重试异常的请求,而不是断路器抛出异常,那么除了断路器策略之外,还需要实施重试策略。我对此的理解是,问题1中的行为会发生,然后会尝试重试。
正确。断路器仍然会抛出BrokenCircuitException
(没有“替代”可以阻止断路器这样做)。但是,如果包装重试策略处理该异常,则 BrokenCircuitException
将不会传播回调用代码。可运行的示例可以在Polly-Samples 或this dotnetfiddle 中找到。
A.如果出现网络中断或服务关闭,并且期望的行为是在网络恢复或服务再次启动时重试请求,则需要执行 RetryForever。实际上会有很多阻塞,对吗?
Polly 策略仅管理在该执行路径上发生的事情,不知道是否存在类似的并行执行。所以是的,如果有一个RetryForever
,并且如果您预计在连接丢失时会在该RetryForever
中循环大量调用,那么在保持模式中的许多操作存在内存/资源膨胀的风险。要知道这是否是您的应用程序/架构的一个重要问题,您需要在代表性环境中进行试验。
有没有更好的方法来做到这一点?
您可以限制重试次数并将失败的发送捕获到某种队列中。连接恢复后,您可以重新发送故障队列中的项目。
【讨论】:
【参考方案2】:我不是 RetryForever 政策的忠实拥护者。如果您有通过 INSERT 语句将 80,000 条记录插入到表中的场景,该怎么办。突然出现网络问题。您很快就会有 80k 异步进程占用您的系统资源。我强烈推荐使用 Retry、BulkHead 和 Circuitbreaker 的策略包装。我编写了一个自定义 SQLExtension 类,该类具有辅助回退连接字符串和回退命令,用于在断路器耗尽的情况下将关键事务写入某些辅助 sql 服务器。还有一点要记住。您可以跟踪特定的 SQL 异常错误,主要是 Duplicates。假设您的 sql 命令不是一个简单的 Insert 命令,而是一个复杂的存储过程。断路器一旦解除,就会简单地重新运行存储过程,并在具有主键的表上生成 Duplicate found 消息。所以你可能想忽略重复的消息。
【讨论】:
以上是关于Polly 断路器/重试以在网络中断后自动重新启动查询的主要内容,如果未能解决你的问题,请参考以下文章