用 Policy.WrapAsync(或 Wrap)将多个策略按执行顺序嵌套包装,外层控整体(如超时),内层处理细节(如重试、熔断);顺序决定生效优先级,类型须一致,异常为最内层原始异常。

多个Polly策略怎么串起来用
直接说结论:用 Policy.WrapAsync(或 Policy.Wrap)把多个策略按执行顺序“包”成一个,不是简单叠加,而是形成嵌套调用链。外层策略控制整体行为(比如超时),内层策略处理具体异常(比如重试、熔断)。
常见错误是把 RetryPolicy 和 CircuitBreakerPolicy 分开 await,结果重试逻辑完全绕过熔断器;或者误用 .Or 想合并策略——那只是扩展异常类型匹配,不是组合策略行为。
-
Wrap的顺序很重要:最外层策略最先生效,最内层策略最接近实际委托。例如TimeoutPolicy.WrapAsync(RetryPolicy.WrapAsync(CircuitBreakerPolicy))表示:先走熔断判断 → 再决定是否重试 → 最后被超时兜底中断 - 所有被包装的策略必须类型一致:要么全是
AsyncPolicy,要么全是AsyncPolicy(无返回值)。混用会导致编译失败 - 如果策略里用了
Context(比如传"operation-id"),外层策略的Context会透传给内层,但各策略的onRetry/onBreak回调中拿到的Context是同一份引用,注意别意外覆盖
带返回值的异步策略怎么包装
处理 HTTP 调用这类有返回值的场景,必须用泛型版本的 WrapAsync,否则编译不通过。策略声明和包装都得对齐类型。
例如封装一个带熔断 + 重试 + 超时的 HttpClient.GetAsync 调用:
var circuitBreaker = Policy
.Handle()
.CircuitBreakerAsync(5, TimeSpan.FromMinutes(1));
var retry = Policy
.Handle()
.OrResult(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2) });
var timeout = Policy.TimeoutAsync(5);
var wrapped = Policy.WrapAsync(circuitBreaker, retry, timeout); // 注意顺序
// 使用时:
var response = await wrapped.ExecuteAsync(() => client.GetAsync("https://www.php.cn/link/710ba53b0d353329706ee1bedf4b9b39"));
关键点:OrResult 用于捕获“成功返回但业务失败”的情况(如 500 响应),它和 Handle 是并列条件,不是嵌套关系;TimeoutAsync 的泛型参数必须和实际委托返回类型严格一致。
同步策略包装要注意什么
同步策略用 Policy.Wrap,但实际项目中极少单独使用——因为现代 C# I/O 几乎都是异步的。唯一常见同步场景是包装本地计算型操作(比如 JSON 解析、内存缓存查找),且明确不希望引入 async/await 开销。
- 不要把异步策略塞进
Policy.Wrap:比如把Policy.Handle传给().RetryAsync() Wrap,编译报错Cannot convert async lambda - 同步包装链里所有策略必须是
Policy或Policy,不能混入AsyncPolicy类型 - 如果原操作是
Func,包装后调用Execute;如果是Action,则用Execute无返回版
策略组合后异常怎么捕获
包装后的策略执行失败时,抛出的异常是**最内层触发策略的原始异常**,不会自动包装成新类型。比如熔断器打开时抛 BrokenCircuitException,超时抛 TimeoutRejectedException,重试耗尽抛最后一次的 HttpRequestException。
这意味着你不能只 catch Exception 就完事,得按需区分处理:
- 想统一记录所有失败?用
ExecuteAndCaptureAsync获取PolicyResult,检查FinalException属性 - 想对超时单独告警?在
ExecuteAsync外层 try/catchTimeoutRejectedException - 熔断状态变化需要监听?必须在定义
CircuitBreakerPolicy时传入onBreak和onReset回调,包装后这些回调依然有效
最容易被忽略的是:当多个策略都配置了 onRetry,它们会按包装顺序从外到内依次触发——但如果你没给每个策略配不同的日志前缀,根本分不清哪条日志属于哪个策略。










