使用 polly 包处理异常列表的通用方法

Generic method which handles exception list with polly package

提问人:Sanman Chavan 提问时间:11/17/2023 最后编辑:Peter CsalaSanman Chavan 更新时间:11/20/2023 访问量:64

问:

我需要以通用方式处理异常列表。我需要以 Polly 风格重写代码。

我有处理自定义泛型异常的现有工作代码:

readonly TimeSpan maxDelay;
readonly int initialRetryDelayInMilliseconds;
readonly double backoffFactor;
readonly int maxAttemptCount;
readonly string methodName;
readonly List<Exception> allowedExceptions;

public ExponentialBackoffService(string _methodName,int _initialRetryDelayInMilliseconds, TimeSpan _maxDelay, double _backoffFactor, int _maxAttemptCount, List<Exception> _allowedExceptions = null)
{
    initialRetryDelayInMilliseconds = _initialRetryDelayInMilliseconds;
    backoffFactor = _backoffFactor;
    maxAttemptCount = _maxAttemptCount;
    maxDelay = _maxDelay;
    methodName = _methodName;
    allowedExceptions = _allowedExceptions;
}

public virtual async Task<T> Do<T>(Func<Task<T>> task)
{
    TraceLogger.Log("ExponentialBackoffService", 0, "ExponentialBackoffService task do", true);

    var exceptions = new List<Exception>();
    for (int attempted = 0; attempted < maxAttemptCount; attempted++)
    {
        var trace = TraceLog.GenerateTraceLog($"ExponentialBackoffService call started==>{attempted}");
        trace.Category = "ExponentialBackoffService";
        await Logger.WriteLogAsync(trace);

        try
        {
            if (attempted > 0)
            {
                TimeSpan retryDelayInMilliseconds = CalculateExponentialBackoff(TimeSpan.FromMilliseconds(initialRetryDelayInMilliseconds), attempted);
                await Task.Delay(retryDelayInMilliseconds);
                await Logger.WriteLogAsync(GenerateTrace(retryDelayInMilliseconds));
            }
            return await task();
        }
        catch (Exception ex)
        {
            if (AllowAllException() || AllowSpecificExceptions(ex))
            {
                exceptions.Add(ex);
            }
            else { throw; }
        }
        finally
        {
            var trace1 = TraceLog.GenerateTraceLog($"ExponentialBackoffService call completed==>{attempted}");
            trace1.Category = "ExponentialBackoffService";
            await Logger.WriteLogAsync(trace1);
        }
    }
    throw new AggregateException(exceptions);
}
 
public virtual bool AllowAllException()
{
    return  allowedExceptions == null || allowedExceptions?.Count() == 0;
}

public virtual bool AllowSpecificExceptions(Exception currentException)
{
    bool isCurrentExceptionAllowed = false;
    if(allowedExceptions?.Count() > 0)
    {
        foreach (Exception exception in allowedExceptions)
        {
            if(exception.GetType()== currentException.GetType())
            {
                isCurrentExceptionAllowed = true;
                break;
            }
        }
        return isCurrentExceptionAllowed;
    }
    return true;
}

需要以 polly 样式重写上面的代码。

第一个问题

var existingPolicy = Policy.WaitAndRetryAsyc(BackOff.DecorrletedJitterBackOffV2)

这里,需要定义每次重试的重试逻辑和日志记录;无异常定义

第二个问题

if(allowedExceptions?.Count() > 0)
{
    for(int i ;i<allowedExceptions.count();i++)
    {
        var dynamicPolicy=Policy.Handle<allowedExceptions[i].GetType>;//create dynamic policy for handle exception
        existingPolicy.WrapAsync(dynamicPolicy);//appending each exception as per exceptionList
    }
}

根据调用方,将发送异常列表,这些通用异常需要添加到策略中

最后执行

existingPolicy.executeAsync(task);
C# 列表 .net-core polly retry-logic

评论


答:

-2赞 wpdnqd 11/17/2023 #1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Polly;
using Polly.Wrap;

public class ExponentialBackoffService
{
    private readonly TimeSpan maxDelay;
    private readonly int initialRetryDelayInMilliseconds;
    private readonly double backoffFactor;
    private readonly int maxAttemptCount;
    private readonly string methodName;
    private readonly List<Exception> allowedExceptions;

    public ExponentialBackoffService(string _methodName, int _initialRetryDelayInMilliseconds, TimeSpan _maxDelay, double _backoffFactor, int _maxAttemptCount, List<Exception> _allowedExceptions = null)
    {
        initialRetryDelayInMilliseconds = _initialRetryDelayInMilliseconds;
        backoffFactor = _backoffFactor;
        maxAttemptCount = _maxAttemptCount;
        maxDelay = _maxDelay;
        methodName = _methodName;
        allowedExceptions = _allowedExceptions;
    }

    public virtual async Task<T> Do<T>(Func<Task<T>> task)
    {
        var retryPolicy = Policy.WrapAsync(CreateRetryPolicies());

        try
        {
            return await retryPolicy.ExecuteAsync(task);
        }
        catch (AggregateException ex)
        {
            // Log aggregate exceptions
            Console.WriteLine($"All retry attempts failed for {methodName}. Aggregating exceptions.");
            throw;
        }
    }

    private IEnumerable<IAsyncPolicy> CreateRetryPolicies()
    {
        var policies = new List<IAsyncPolicy>();

        for (int i = 0; i < allowedExceptions?.Count; i++)
        {
            var exceptionType = allowedExceptions[i].GetType();
            var dynamicPolicy = Policy.Handle(exceptionType)
                .WaitAndRetryAsync(maxAttemptCount, attempt => CalculateExponentialBackoff(TimeSpan.FromMilliseconds(initialRetryDelayInMilliseconds), attempt),
                    (exception, timeSpan, retryCount, context) =>
                    {
                        // Log for each retry attempt
                        Console.WriteLine($"Retry attempt {retryCount} failed for {methodName}. Retrying in {timeSpan.TotalMilliseconds} ms.");
                    });

            policies.Add(dynamicPolicy);
        }

        return policies;
    }

    private TimeSpan CalculateExponentialBackoff(TimeSpan initialDelay, int retryAttempt)
    {
        // Calculate exponential backoff with jitter
        var delay = TimeSpan.FromMilliseconds(Math.Min(initialDelay.TotalMilliseconds * Math.Pow(backoffFactor, retryAttempt), maxDelay.TotalMilliseconds));
        var jitter = new Random().Next(0, (int)(delay.TotalMilliseconds * 0.1)); // 10% jitter
        return delay.Add(TimeSpan.FromMilliseconds(jitter));
    }
}

评论

1赞 Sanman Chavan 11/17/2023
看来您只复制粘贴了问题
0赞 wpdnqd 11/17/2023
养成先阅读的习惯。
0赞 Sanman Chavan 11/17/2023
对不起,我的错。让我试试
0赞 Peter Csala 11/17/2023
@wpdnqd 此代码将无法编译。var dynamicPolicy = Policy.Handle(exceptionType)
2赞 BDL 11/17/2023
这个答案可能真的需要一些解释。
1赞 Peter Csala 11/17/2023 #2

让我先试着回答你的第二个问题。

既然你是,这就是为什么你可以这样写:allowedExceptionsList<Exception>

Policy.Handle<Exception>(ex => allowedExceptions.Contains(ex))
      ...

或者只是

Policy.Handle<Exception>(allowedExceptions.Contains)
      ...

在这两种情况下,您都定义了一个策略,该策略仅在引发异常时触发,并且该异常是允许的异常之一。


现在让我们关注你的第一个问题。

在 Polly V7 中,您可以定义一个策略,如果抛出特定异常 (/) 或结果对象处于给定状态 ()。Handle<TEx>HandleInner<TEx>HandleResult<TResult>(Predicate p)

因此,如果要无条件重试 n 次,则可以执行以下解决方法:

Policy.HandleResult<object?>(_ => true)

这意味着要修饰的方法需要返回引用类型实例或 null。

或者,您可以定义以下触发器:bool

Policy.HandleResult<bool>(_ => true)

这取决于你喜欢哪一个。这是一种解决方法,因为 Polly V7 不支持无条件触发。