基于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
相关产品推荐
相关产品推荐

