You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Polly包重构通用异常列表处理逻辑的技术咨询

用Polly重构通用异常重试逻辑

完整重构代码

将原自定义指数退避重试逻辑替换为Polly风格实现,保留原日志、异常过滤和抖动退避行为:

using Polly;
using Polly.Retry;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class PollyBackoffService
{
    private readonly AsyncRetryPolicy _retryPolicy;
    private readonly string _methodName;

    public PollyBackoffService(string methodName, int initialRetryDelayInMilliseconds, TimeSpan maxDelay, int maxAttemptCount, List<Type> allowedExceptionTypes = null)
    {
        _methodName = methodName;

        // 构建抖动退避策略:总尝试次数为maxAttemptCount,因此重试次数为maxAttemptCount-1
        var backoffIntervals = Backoff.DecorrelatedJitterBackoffV2(
            medianFirstRetryDelay: TimeSpan.FromMilliseconds(initialRetryDelayInMilliseconds),
            retryCount: maxAttemptCount - 1,
            maxDelay: maxDelay);

        // 构建带日志和异常过滤的重试策略
        _retryPolicy = Policy
            .Handle<Exception>(ex => IsExceptionAllowed(ex, allowedExceptionTypes))
            .WaitAndRetryAsync(
                sleepDurations: backoffIntervals,
                onRetryAsync: async (exception, delay, retryCount, context) =>
                {
                    // 记录重试触发日志
                    var retryTrace = TraceLog.GenerateTraceLog($"PollyBackoffService retry triggered: attempt {retryCount}, delay {delay.TotalMilliseconds}ms");
                    retryTrace.Category = "PollyBackoffService";
                    await Logger.WriteLogAsync(retryTrace);
                });
    }

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

        // 包装任务,添加每次尝试的开始/结束日志
        var loggedTask = async (Context context) =>
        {
            int attemptNumber = GetCurrentAttemptNumber(context);
            var startTrace = TraceLog.GenerateTraceLog($"PollyBackoffService call started==>{attemptNumber}");
            startTrace.Category = "PollyBackoffService";
            await Logger.WriteLogAsync(startTrace);

            try
            {
                return await task();
            }
            finally
            {
                var endTrace = TraceLog.GenerateTraceLog($"PollyBackoffService call completed==>{attemptNumber}");
                endTrace.Category = "PollyBackoffService";
                await Logger.WriteLogAsync(endTrace);
            }
        };

        return await _retryPolicy.ExecuteAsync(loggedTask);
    }

    private bool IsExceptionAllowed(Exception ex, List<Type> allowedExceptionTypes)
    {
        // 允许所有异常:列表为空或未传入
        if (allowedExceptionTypes == null || allowedExceptionTypes.Count == 0)
            return true;

        // 检查当前异常类型是否在允许列表中
        return allowedExceptionTypes.Contains(ex.GetType());
    }

    private int GetCurrentAttemptNumber(Context context)
    {
        const string attemptKey = "CurrentAttempt";
        if (!context.TryGetValue(attemptKey, out var attemptObj) || !(attemptObj is int attempt))
        {
            attempt = 0;
            context[attemptKey] = attempt;
        }
        else
        {
            attempt++;
            context[attemptKey] = attempt;
        }
        return attempt;
    }
}

问题1:添加重试日志与抖动退避逻辑

Polly的WaitAndRetryAsync直接支持结合Backoff.DecorrelatedJitterBackoffV2生成抖动退避间隔,同时通过onRetryAsync回调实现重试日志记录:

  • 退避间隔生成:通过Backoff.DecorrelatedJitterBackoffV2传入初始延迟、最大延迟和重试次数(注意重试次数=总尝试次数-1)
  • 重试日志:在onRetryAsync中记录重试触发的次数、延迟时间等信息
  • 每次尝试的开始/结束日志:通过包装任务,利用Polly的Context传递当前尝试次数,实现原代码的日志逻辑

问题2:动态添加允许的异常类型

推荐实现:谓词判断

直接在Handle<Exception>中传入自定义谓词函数IsExceptionAllowed,实现动态异常过滤:

  • 若未传入允许的异常列表或列表为空,允许所有异常重试
  • 若传入列表,检查当前异常的类型是否在列表中

原思路的动态策略组合实现

如果需要按原思路逐个创建异常策略再组合,可使用以下方式:

// 初始化空策略
AsyncPolicy exceptionFilterPolicy = Policy.NoOpAsync();

if (allowedExceptionTypes?.Count > 0)
{
    foreach (var exceptionType in allowedExceptionTypes)
    {
        // 动态创建针对单个异常类型的策略
        var specificExceptionPolicy = Policy
            .HandleException(exceptionType)
            .NoOpAsync();

        // 组合策略:允许列表中任意一种异常触发重试
        exceptionFilterPolicy = exceptionFilterPolicy.OrAsync(specificExceptionPolicy);
    }
}
else
{
    // 允许所有异常
    exceptionFilterPolicy = Policy.Handle<Exception>().NoOpAsync();
}

// 将异常过滤策略与重试策略组合
_retryPolicy = exceptionFilterPolicy.WrapAsync(Policy
    .WaitAndRetryAsync(
        backoffIntervals,
        onRetryAsync: async (exception, delay, retryCount, context) =>
        {
            var retryTrace = TraceLog.GenerateTraceLog($"PollyBackoffService retry triggered: attempt {retryCount}, delay {delay.TotalMilliseconds}ms");
            retryTrace.Category = "PollyBackoffService";
            await Logger.WriteLogAsync(retryTrace);
        }));

内容的提问来源于stack exchange,提问作者Sanman Chavan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:35:07