Polly Circuit Breaker / Retry 在网络中断后自动重启查询

Polly Circuit Breaker / Retry to automatically restart queries after a network outage

我正在 .NET Framework 4.5.2 中通过 Polly 实现断路器和重试模式。

我想看看我的理解是否正确。

问题1:如果网络中断,断路器达到exceptionsAllowedBeforeBreaking个数,进入打开状态,等待durationOfBreak时间段,电路会为新的请求打开,但是已经发送的请求会抛出异常吗?

问题 2:如果期望的行为是重试那些有异常的请求,而不是断路器抛出异常,那么除了断路器策略之外,还需要实施重试策略。我的理解是会出现问题1中的行为,然后重试。

一个。如果出现网络中断或服务中断,并且所需的行为是在网络恢复或服务再次启动后立即重试请求,则需要执行 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 将设置为取消,此时所有请求都会失败。如果在此之前网络已恢复,则会重试请求。

Question 1: If there is a network outage and the circuit breaker has reached the exceptionsAllowedBeforeBreaking number, gone into the open state and waited for the durationOfBreak period, the circuit will be open for new requests ...

经过 durationOfBreak 后,电路将转换为半开状态,在此期间 a single trial call is permitted (in the current implementation)

... but those that have been sent will throw an exception?

placed during the Open state will throw BrokenCircuitException 次通话。

Question 2: If the desired behavior is for those requests that had exceptions to be retried instead of the circuit breaker throwing an exception then the Retry policy needs to be implemented in addition to the Circuit Breaker policy. My understanding of this is that the behavior in question 1 would occur, and then the retry would be attempted.

正确。断路器仍会抛出 BrokenCircuitException(没有 'instead' 会阻止断路器这样做)。但是,如果包装重试策略处理该异常,则 BrokenCircuitException 将不会传播回调用代码。可以在 Polly-Samples or this dotnetfiddle.

中找到可运行的示例

A. If there is a network outage or the service is down and the desired behavior is for a request to be retried as soon as the network is restored or the service is up again, a RetryForever would need to be performed. Effectively there would be lots of blocking, correct?

Polly 策略只管理该执行路径上发生的事情,不知道是否有类似的并行执行。所以是的,如果有一个 RetryForever 并且如果您预计在连接丢失时会有大量调用在 RetryForever 中循环,则存在 memory/resource 膨胀的风险,其中有许多操作控股模式。要了解这是否是您 application/architecture 的一个重要问题,您需要在代表性环境中进行试用。

Is there a better way of doing this?

您可以限制重试次数并将失败的发送捕获到某种队列中。连接恢复后,您可以重新发送失败队列中的项目。

我不太喜欢 RetryForever 政策。如果您遇到通过 INSERT 语句将 80,000 条记录插入到 table 中的情况,该怎么办?突然出现网络问题。您很快就会有 8 万个异步进程占用您的系统资源。我强烈推荐使用 Retry、BulkHead 和 Circuitbreaker 的策略包装。我写了一个自定义 SQLExtension class,它有一个 secondaryfallback connectionstring 和 fallback 命令,用于在断路器耗尽时将关键事务写入一些辅助 sql 服务器。还有一点要记住。您可以跟踪特定的 SQL 异常错误,主要是重复项。假设您的 sql 命令不是简单的 Insert 命令,而是一个复杂的存储过程。断路器一旦解除,将简单地重新 运行 存储过程,并在 table 上使用主键生成 Duplicate found 消息。所以您可能希望忽略重复的消息。