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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 19:05:32