繁体   English   中英

记录异常并重新抛出的 Polly 策略

[英]Polly policy to log exception and rethrow

我考虑使用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 with throw。

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

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

如果您已经有 Polly,则可以按照您建议的方式安全地重新调整FallbackPolicy的用途。 onFallback委托和回退操作或值不受.Handle<>()子句控制,因此您可以安全地从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 的结构中编写自己的LogThenRethrowPolicy 此提交(添加了简单的NoOpPolicy )举例说明了添加新策略所需的最低限度。 您可以添加类似于NoOpPolicy的实现,但只需try { } catch { /* log; rethrow */ } try { } catch { /* log; rethrow */ }


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

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}");
    }
);

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

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);

        });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM