【问题标题】:Retries are not happening after the first retry on a broken circuit在断路的第一次重试后没有发生重试
【发布时间】:2018-12-27 07:15:32
【问题描述】:

我有以下政策:

var sharedBulkhead = Policy.BulkheadAsync(
            maxParallelization: maxParallelizations, 
            maxQueuingActions: maxQueuingActions,
            onBulkheadRejectedAsync: (context) =>
            {
                Log.Info($"Bulk head rejected => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
                return TaskHelper.EmptyTask;
            }
        );

var retryPolicy = Policy.Handle<Exception>(e => (e is HttpRequestException)).WaitAndRetryAsync(
            retryCount: maxRetryCount,
            sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),
            onRetryAsync: (exception, calculatedWaitDuration, retryCount, context) =>
            {
                Log.Error($"Retry => Count: {retryCount}, Wait duration: {calculatedWaitDuration}, Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}, Exception: {exception}.");
                return TaskHelper.EmptyTask;
            });

            var circuitBreaker = Policy.Handle<Exception>(e => (e is HttpRequestException)).CircuitBreakerAsync(
            exceptionsAllowedBeforeBreaking: maxExceptionsBeforeBreaking, 
            durationOfBreak: TimeSpan.FromSeconds(circuitBreakDurationSeconds), 
            onBreak: (exception, timespan, context) =>
            {
                Log.Error($"Circuit broken => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}, Exception: {exception}");
            },
            onReset: (context) =>
            {
                Log.Info($"Circuit reset => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
            }
        );

var fallbackForCircuitBreaker = Policy<bool>
         .Handle<BrokenCircuitException>()
         .FallbackAsync(
             fallbackValue: false,
             onFallbackAsync: (b, context) =>
             {
                 Log.Error($"Operation attempted on broken circuit => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
                 return TaskHelper.EmptyTask;
             }
         );

var fallbackForAnyException = Policy<bool>
            .Handle<Exception>()
            .FallbackAsync(
                fallbackAction: (ct, context) => { return Task.FromResult(false); },
                onFallbackAsync: (e, context) =>
                {
                    Log.Error($"An unexpected error occured => Policy Wrap: {context.PolicyWrapKey}, Policy: {context.PolicyKey}, Endpoint: {context.OperationKey}");
                    return TaskHelper.EmptyTask;
                }
            );


var resilienceStrategy = Policy.WrapAsync(retryPolicy, circuitBreaker, sharedBulkhead);
        var policyWrap = fallbackForAnyException.WrapAsync(fallbackForCircuitBreaker.WrapAsync(resilienceStrategy));

我这样执行策略:

Task.Run(() =>
        {
            foreach (var changeMessage in changeMessages)
            {
                policyWrap.ExecuteAsync((context) => CallApi(changeMessage), new Context(endPoint));
            }
        });

在第一次调用 fallbackForCircuitBreaker 后不会发生重试。无论电路处于何种状态,我都希望根据等待持续时间进行重试。为什么这不起作用?

【问题讨论】:

  • 发布的代码正在执行异步任务而不等待它们:await Task Run(...)await policyWrap.ExecuteAsync(...) 会更常见 - 除非您有意触发并忘记异步任务。如果打算使用即发即弃,则可能值得考虑代码应该如何处理异常,否则这些异常会变成unobserved task exceptions
  • 是的。那是故意的。 fallbackForAnyException不会捕获到未处理的异常吗?
  • fallbackForAnyException 不会捕获未处理的异常,因为在发布的代码中没有以任何方式观察到代表异步工作的 Tasks 的结果。我建议阅读 any general articles on exception handling in async codepreviously linked article 以深入讨论未观察到的任务异常。

标签: c# asp.net polly


【解决方案1】:

在第一次调用 fallbackForCircuitBreaker 后不会发生重试。无论电路处于何种状态,我都希望根据等待持续时间进行重试。

重试策略未配置为处理BrokenCircuitException,仅处理HttpRequestException

要使重试策略也为BrokenCircuitException 重试,请配置策略:

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<BrokenCircuitException>()
    .WaitAndRetryAsync( /* etc */ );

注意:如果只进行上述更改,则问题中发布的PolicyWrap中的策略排序:

.., fallbackForCircuitBreaker, retryPolicy, circuitBreaker, sharedBulkhead

意味着fallbackForCircuitBreaker 将仅在所有重试失败并且最后一次重试以BrokenCircuitException 失败时被调用。见PolicyWrap documentation

【讨论】:

  • 应该进行哪些更改才能在每次对断路进行重试时调用fallbackForCircuitBreaker
  • 还有一个问题,我已将sharedBulkhead 声明为服务中的实例属性。它在构造函数中初始化。这是一个好习惯吗?
  • 请将这些问题作为单独的 Stack Overflow 问题提出; Stack Overflow 更喜欢我们避免在 cmets 中回答问题,而且答案对于 cmets 来说可能太长了。
  • 会这样做。同时,你能看看这个:stackoverflow.com/questions/53942022/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-13
  • 2017-11-28
  • 2013-08-18
  • 2023-02-03
  • 1970-01-01
  • 2020-09-30
  • 2019-07-02
相关资源
最近更新 更多