C# Polly策略包装方法 C#如何组合多个Polly策略

来源:这里教程网 时间:2026-02-21 17:41:11 作者:

多个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
,它们会按包装顺序从外到内依次触发——但如果你没给每个策略配不同的日志前缀,根本分不清哪条日志属于哪个策略。

相关推荐