【问题标题】:Polly policy to log exception and rethrow用于记录异常并重新抛出的 Polly 策略
【发布时间】:2017-03-22 12:46:52
【问题描述】:

我考虑使用Polly 创建策略来记录异常并重新抛出。 我没有找到允许它开箱即用的现有方法,但我看到的一些选项是

后备

// Specify a substitute value or func, calling an action (e.g. for logging)
// if the fallback is invoked.
Policy.Handle<Whatever>()
.Fallback<UserAvatar>(UserAvatar.Blank,
    onFallback: (exception, context) =>
    {
        _logger.Log(exception, context);
        throw exception;
    });

问题:Fallback 可以抛出异常吗?

超时

Policy.Timeout(1, T30meoutStrategy.Pessimistic,
(context, timespan, task) =>
{
    // ContinueWith important!: the abandoned task may very well still be executing,
    // when the caller times out on waiting for it!
    task.ContinueWith(t =>
    {
        if (t.IsFaulted)
        {
            logger.Error(context,t.Exception);
            throw exception;
        }
    });
}

重试

Policy.Handle<DivideByZeroException>().Retry(0,
(exception, retryCount) =>
{
    logger.Error(context,exception);
    throw exception;
});

问题:是否支持 0 次重试?

或者 KISS 并自己编写 简单的 try/catch 和 throw。

这些方法中哪一种更好? 你有什么建议?

【问题讨论】:

    标签: c# exception polly


    【解决方案1】:

    如果您还没有 Polly,try/catch 似乎最简单。

    如果您已经有 Polly,FallbackPolicy 可以按照您建议的方式安全地重新使用。 onFallback 委托和回退操作或值 are not governed by the .Handle&lt;&gt;() clauses of the Policy,因此您可以安全地从 onFallback 委托中重新引发异常。

    Policy<UserAvatar>.Handle<Whatever>()
    .Fallback<UserAvatar>(UserAvatar.Blank,
        onFallback: (exception, context) =>
        {
            _logger.Log(exception, context);
            throw exception;
        });
    

    您的问题使用TimeoutPolicy 概述的方法只会捕获调用者之前因超时而离开的代表抛出的异常,并且仅在TimeoutMode.Pessimistic 中;并非所有例外。


    您的问题用.Retry(0, ...) 概述的方法不起作用。如果未指定重试,则不会调用 onRetry 委托。


    为了避免重复使用 FallbackPolicy 造成的不整洁,您还可以在 Polly 的结构中编写自己的 LogThenRethrowPolicyThis commit(添加了简单的NoOpPolicy)举例说明了添加新策略所需的最低要求。您可以添加类似于NoOpPolicy 的实现,但只需添加try { } catch { /* log; rethrow */ }


    2019 年 1 月编辑:Polly.Contrib 现在还包含一个 Polly.Contrib.LoggingPolicy 可以帮助解决此问题。

    【讨论】:

    • 你知道用于 LoggingPolicy 的 NuGet 包在哪里吗?我在 GitHub 上没有看到链接,我尝试在 NuGet 中搜索 Polly.Contrib 和 Polly.Contrib.LoggingPolicy。这两个包似乎都不存在,尽管我发现 Polly.Contrib 的一些子包似乎不相关。
    【解决方案2】:

    https://github.com/App-vNext/Polly-Samples/blob/master/PollyDemos/Async/AsyncDemo02_WaitAndRetryNTimes.cs 表明您可以使用onRetry: 选项,至少对于 WaitAndRetryAsync 是这样。其他的我还没看。

    HttpPolicyExtensions
    .HandleTransientHttpError()
    .WaitAndRetryAsync(3,
        retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))  // exponential back-off: 2, 4, 8 etc
                        + TimeSpan.FromMilliseconds(Jitterer.Next(0, 1000)), // plus some jitter: up to 1 second
        onRetry: (response, calculatedWaitDuration) =>
        {
            logger.LogError($"Failed attempt. Waited for {calculatedWaitDuration}. Retrying. {response.Exception.Message} - {response.Exception.StackTrace}");
        }
    );
    

    【讨论】:

    • 我不想重试,只是记录并重新抛出。
    【解决方案3】:

    这是我使用通用方法的解决方案

    public async Task<T> PollyRetry<T>(
            Func<Task<T>> action)
        {
    
            bool hasFallback = false;
            Exception ex = null;
    
            var fallbackPolicy = Policy<T>.Handle<Exception>().FallbackAsync(
                default(T), d =>
                {
                    //log final exception
    
                    ex = d.Exception;
    
                    hasFallback = true;
                    return Task.FromResult(new { });
    
                });
    
            var retryPolicy = Policy
                .Handle<Exception>()
                .WaitAndRetryAsync(3,
                    retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                    (res, timeSpan, context) =>
                    {
                        //log exception
                    });
    
            var policyResult = await fallbackPolicy.WrapAsync(retryPolicy).ExecuteAndCaptureAsync(action);
    
            if (hasFallback && ex != null)
                throw ex;
    
            return policyResult.Result;
        }
    

    //call service with retry logic
            TestResponse response = await _pollyRetryService.PollyRetry(async () =>
            {
                return await _testService.Test(input);
    
            });
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-02
    • 2015-03-23
    • 2012-02-21
    • 2015-11-18
    • 2019-05-05
    • 2017-05-17
    • 2016-08-20
    相关资源
    最近更新 更多