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