如何让Polly重试策略支持自定义异常处理器
问题描述
我有如下Polly重试策略的C#实现代码,请问如何修改该策略,使其允许用户指定自定义异常处理器,例如类似以下的处理器:
.Handle<WebSocketException>(exception => IsWebSocketErrorRetryEligible(exception))
原代码片段
public static async Task DoAsync(Func<Task> action, TimeSpan retryInterval, int retryCount = 3) { await DoAsync<object?>(async () => { await action(); return null; }, retryInterval, retryCount); } public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 0) { var policyResult = await Policy .Handle<Exception>() .WaitAndRetryAsync(retryCount, retryAttempt => retryWait) .ExecuteAndCaptureAsync(action); if (policyResult.Outcome == OutcomeType.Failure) { throw policyResult.FinalException; } return policyResult.Result; } private bool IsWebSocketErrorRetryEligible(WebSocketException wex) { if (wex.InnerException is HttpRequestException) { // 假设是瞬时故障 return true; } return wex.WebSocketErrorCode switch { WebSocketError.ConnectionClosedPrematurely => true, // 可能是网络波动 WebSocketError.Faulted => true, // 可能是服务器错误或其他临时问题 WebSocketError.HeaderError => true, // 可能是瞬时服务器错误 WebSocketError.InvalidMessageType => false, WebSocketError.InvalidState => false, WebSocketError.NativeError => true, // 可能是瞬时服务器错误 WebSocketError.NotAWebSocket => Regex.IsMatch(wex.Message, @"\b(5\d\d|408)\b"), // 5xx错误 + 超时 WebSocketError.Success => true, // 理论上不会发生,但仍重试 WebSocketError.UnsupportedProtocol => false, WebSocketError.UnsupportedVersion => false, _ => throw new ArgumentOutOfRangeException(nameof(wex)) }; }
解决方案
我们可以通过重载原有方法,添加自定义参数来支持用户指定异常处理器,以下提供两种实用方案:
方案一:通用异常判断委托(简单场景)
新增重载方法,传入Predicate<Exception>类型的参数,用于自定义重试的异常判断逻辑,同时兼容原有默认行为:
// 无返回值方法的重载,新增异常判断参数 public static async Task DoAsync(Func<Task> action, TimeSpan retryInterval, int retryCount = 3, Predicate<Exception>? shouldRetry = null) { await DoAsync<object?>(async () => { await action(); return null; }, retryInterval, retryCount, shouldRetry); } // 带返回值方法的重载,新增异常判断参数 public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 0, Predicate<Exception>? shouldRetry = null) { // 默认处理所有异常,保持原有逻辑 var retryCondition = shouldRetry ?? (ex => true); var policyResult = await Policy .Handle(retryCondition) // 应用自定义异常判断 .WaitAndRetryAsync(retryCount, retryAttempt => retryWait) .ExecuteAndCaptureAsync(action); if (policyResult.Outcome == OutcomeType.Failure) { throw policyResult.FinalException; } return policyResult.Result; } // 将原私有方法改为公开/内部,供外部调用 public bool IsWebSocketErrorRetryEligible(WebSocketException wex) { if (wex.InnerException is HttpRequestException) { return true; } return wex.WebSocketErrorCode switch { WebSocketError.ConnectionClosedPrematurely => true, WebSocketError.Faulted => true, WebSocketError.HeaderError => true, WebSocketError.InvalidMessageType => false, WebSocketError.InvalidState => false, WebSocketError.NativeError => true, WebSocketError.NotAWebSocket => Regex.IsMatch(wex.Message, @"\b(5\d\d|408)\b"), WebSocketError.Success => true, WebSocketError.UnsupportedProtocol => false, WebSocketError.UnsupportedVersion => false, _ => throw new ArgumentOutOfRangeException(nameof(wex)) }; }
使用示例
// 传入WebSocket异常的自定义判断逻辑 await YourRetryClass.DoAsync( () => YourWebSocketOperationAsync(), TimeSpan.FromSeconds(1), retryCount: 3, shouldRetry: ex => ex is WebSocketException wex && YourRetryClass.IsWebSocketErrorRetryEligible(wex) );
方案二:灵活的Policy配置(复杂场景)
如果需要支持更复杂的重试规则(比如同时处理多种异常、判断返回值等),可以允许用户传入委托直接配置Polly的PolicyBuilder:
// 无返回值方法的重载,新增Policy配置委托 public static async Task DoAsync(Func<Task> action, TimeSpan retryInterval, int retryCount = 3, Func<PolicyBuilder, PolicyBuilder>? configurePolicy = null) { await DoAsync<object?>(async () => { await action(); return null; }, retryInterval, retryCount, configurePolicy); } // 带返回值方法的重载,新增Policy配置委托 public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 0, Func<PolicyBuilder, PolicyBuilder>? configurePolicy = null) { var policyBuilder = Policy.Handle<Exception>(); // 默认基础配置 if (configurePolicy != null) { policyBuilder = configurePolicy(policyBuilder); // 应用用户自定义配置 } var policyResult = await policyBuilder .WaitAndRetryAsync(retryCount, retryAttempt => retryWait) .ExecuteAndCaptureAsync(action); if (policyResult.Outcome == OutcomeType.Failure) { throw policyResult.FinalException; } return policyResult.Result; }
使用示例
完全兼容Polly原生API,可叠加多种重试条件:
await YourRetryClass.DoAsync( () => YourWebSocketOperationAsync(), TimeSpan.FromSeconds(1), retryCount: 3, configurePolicy: builder => builder .Handle<WebSocketException>(wex => YourRetryClass.IsWebSocketErrorRetryEligible(wex)) .Or<HttpRequestException>() // 同时处理HttpRequestException );
内容的提问来源于stack exchange,提问作者nop
相关产品推荐
相关产品推荐

