[Polly智能维护网络] 弹性管道 | 弹性管道构建器 | new | Add | .Build()
第2章:弹性管道
在第1章中,我们学习了**弹性策略**——诸如"失败时重试"或"超时停止"等独立的"安全规则"。这些策略本身很强大,但如果我们需要对同一操作应用多个安全规则呢?
想象我们正在拨打一个重要电话:
- 首先,我们希望确保不会无限等待对方接听(超时规则)
- 如果线路忙,我们想再试几次(重试规则)
- 如果持续听到忙音,可能希望暂停拨打以免打扰对方或自己(断路器规则)
我们不会随机应用这些规则,而是有特定计划:先检查超时
,然后重试
,最后触发断路器
。这种组合和顺序至关重要。
这正是Polly中弹性管道的作用。
什么是弹性管道?
将弹性管道视为代码的终极"安全卫士"或完整"战术手册"。
当代码需要执行高风险操作(如通过互联网与其他服务通信
)时,我们将任务交给这个安全卫士。
管道会按照特定顺序应用所有安全规则(配置的**弹性策略**对象),确保操作尽可能可靠。
它是Polly的核心部分,实际执行代码的同时用所有弹性逻辑包裹代码。
弹性管道本质上是由**弹性策略**对象按特定顺序组成的链条。
每个策略像包裹核心操作的一层,在将操作传递给链中下一个策略或最终实际代码前应用其规则。
为什么需要管道?
我们需要管道来实现:
- 组合策略:对单个操作应用多个"安全规则"
- 排序策略:确保安全规则按正确顺序应用。例如通常希望先
Timeout
再Retry
,避免重试已超时的操作 - 集中执行:提供单一入口点自动运行带有所有弹性逻辑的代码
构建和使用弹性管道
要使用弹性管道,首先需要通过添加所需的**弹性策略**对象来构建它。
Polly提供了专用工具弹性管道构建器(下章详述),帮助我们轻松创建管道。
让我们创建一个先Timeout
操作再Retry
失败操作(可能因网络故障)的管道:
using Polly;
using Polly.Retry;
using Polly.Timeout;// 1. 创建弹性管道构建器
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()// 2. 按应用顺序添加策略(最外层先添加)// 这里:超时(外层)-> 重试(内层)-> 实际代码.AddTimeout(TimeSpan.FromSeconds(2)) // 操作超过2秒则停止.AddRetry(new RetryStrategyOptions{MaxRetryAttempts = 2, // 最多重试2次Delay = TimeSpan.FromSeconds(0.5) // 重试间隔0.5秒}).Build(); // 3. 构建管道!
这里发生了什么?
- 使用
new ResiliencePipelineBuilder()
开始构建管道 .AddTimeout(TimeSpan.FromSeconds(2))
添加超时策略,作为最外层。如果整个操作(包括所有重试)超过2秒将被停止.AddRetry(...)
添加重试策略,作为内层,紧邻实际代码。如果代码失败,重试策略将介入.Build()
完成管道构建,创建可立即使用的ResiliencePipeline
对象
现在通过这个管道执行一些不可靠代码:
// 此变量帮助模拟偶发故障
int attemptCount = 0;try
{await pipeline.ExecuteAsync(async token =>{attemptCount++;Console.WriteLine($"尝试 {attemptCount}: 调用外部服务...");if (attemptCount < 2){// 模拟瞬时故障(如网络故障)throw new HttpRequestException("服务不可用!");}// 模拟第三次尝试成功但缓慢的操作await Task.Delay(TimeSpan.FromSeconds(1), token);Console.WriteLine("尝试成功!");}, CancellationToken.None);Console.WriteLine("操作成功完成!");
}
catch (Exception ex)
{Console.WriteLine($"操作完全失败: {ex.Message}");
}
运行时会发生什么?
- 尝试1:
pipeline
调用代码。attemptCount
为1,抛出HttpRequestException
Retry
策略捕获异常。因MaxRetryAttempts
为2,等待0.5秒后调度重试- 尝试2:
pipeline
(具体是Retry
策略)再次调用代码。attemptCount
为2,再次抛出HttpRequestException
Retry
策略再次捕获。还剩一次重试机会,等待0.5秒后调度重试- 尝试3:
pipeline
最后一次调用代码。attemptCount
为3。此次无异常,执行Task.Delay(1)
Timeout
策略从尝试1开始运行的计时器确认整个过程(尝试1+0.5秒延迟+尝试2+0.5秒延迟+尝试3+1秒延迟)在2秒限制内完成- 操作成功完成,显示"操作成功完成!"
如果第三次尝试的Task.Delay
更长(如5秒),Timeout
策略最终会介入并抛出TimeoutRejectedException
,证明两个策略按定义顺序协同工作。
弹性管道内部工作原理
弹性管道通过为操作创建分层"洋葱"或"责任链"工作。
当通过管道执行时,请求从最外层到最内层逐个通过策略。每个策略在实际代码周围添加其逻辑。
以下是操作通过含超时
和重试
策略管道的简化流程:
在Polly源码中,ResiliencePipelineBuilder
通过内部链接每个**弹性策略**的ExecuteCore
方法构建此链条。调用.AddStrategy(...)
时,它在先前添加的策略(或实际代码)周围添加新"包装层"。
参考samples/Intro/Program.cs
创建组合管道的方式:
using Polly;
using Polly.Retry;
using Polly.Timeout;ResiliencePipeline pipeline = new ResiliencePipelineBuilder()// 先添加=最外层策略.AddRetry(new RetryStrategyOptions { /* ...选项... */ })// 后添加=内层策略.AddTimeout(new TimeoutStrategyOptions { /* ...选项... */ }).Build();
虽然代码中AddRetry
出现在AddTimeout
前,此示例与之前不同,展示策略顺序可配置。
-
ResiliencePipelineBuilder
的经验法则是:最先Add
的策略是最外层,最后Add
的策略是最内层,最接近实际代码。 -
构建器将它们
链式
连接,当调用ExecuteAsync
时,调用先通过最外层策略,然后是下一个,依此类推,直到实际操作。
Polly的
ResiliencePipeline
及其构建器管理此执行链,确保所有配置的"安全规则"正确有序应用。
结论
本章我们理解了弹性管道是Polly的核心组件,用于按特定顺序应用多个**弹性策略**对象执行代码。
它像"安全卫士"通过链式组合Timeout
和Retry
等策略确保操作可靠。
既然知道弹性管道是什么及为何重要,我们如何更详细地创建和配置这些强大管道呢?
这正是下章要探讨的:弹性管道构建器。
第3章:弹性管道构建器
在第2章中,我们学习了**弹性管道**。我们发现它就像一个"安全卫士",按照特定顺序应用多个"安全规则"(弹性策略对象)来使我们的代码更健壮。
但如何创建这个强大的安全卫士呢?是手动组装
它的各个部分吗?这听起来很复杂,特别是当有很多规则时!
什么是弹性管道构建器?
想象我们想构建一个具有特定功能的定制"安全卫士"。
我们不会随便抓一堆工具就开始敲打,而是会聘请一位架构师或设计师,他懂得如何将不同功能组合成一个完整可用的系统。
在Polly中,弹性管道构建器正是这样的架构师!它是一个特殊工具,帮助我们设计和*构建**弹性管道**。
我们不需要直接创建复杂的管道,而是使用构建器逐步指定哪些"安全规则"(或[弹性策略]应该成为它的一部分
。完成指定后,构建器会为我们构造出完整、可直接使用的管道。
它解决了将多个弹性策略对象轻松组合并排序成单一、连贯的**弹性管道**的问题。
如何使用弹性管道构建器
使用构建器就像遵循食谱:
- 开始设计:创建一个新的
ResiliencePipelineBuilder
对象。这就像获得一张空白蓝图。 - 添加规则(策略):然后逐个添加所需的弹性策略对象。每个
Add
方法(如AddTimeout
或AddRetry
)都会在蓝图中添加一个新的安全规则。 - 完成设计:添加完所有规则后,调用
.Build()
方法。这会告诉构建器根据蓝图构造最终的**弹性管道**对象。
让我们看看实际操作:
using Polly;
using Polly.Timeout; // 用于AddTimeout
using Polly.Retry; // 用于AddRetry// 1. 开始设计"安全卫士"(弹性管道)
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()// 2. 添加规则,最外层先添加// 规则1:整体超时(外层).AddTimeout(TimeSpan.FromSeconds(5))// 规则2:重试(内层),如果第一次尝试失败.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 2 })// 3. 完成设计,构建安全卫士!.Build();// 现在有了一个可立即使用的'pipeline'对象,
// 就像我们在第2章中做的那样
Console.WriteLine("弹性管道设计并构建完成!");
这段代码发生了什么?
new ResiliencePipelineBuilder()
:我们创建构建器实例。可以想象为打开安全卫士的设计软件。.AddTimeout(TimeSpan.FromSeconds(5))
:告诉构建器"首先添加一个规则,如果操作超过5秒就停止"。这成为安全卫士的最外层。.AddRetry(new RetryStrategyOptions { MaxRetryAttempts = 2 })
:接着告诉构建器"然后添加一个规则,如果操作失败最多重试2次"。这成为内层,更接近实际代码。.Build()
:最后告诉构建器"好了,所有规则都已就位,现在把它们组合起来,给我完整的**弹性管道**对象"
使用构建器Add
策略的顺序非常重要:
第一个添加的策略将成为管道的最外层,最后一个添加的策略将成为最内层,最接近实际代码。
弹性管道构建器的内部工作原理
ResiliencePipelineBuilder
的工作方式像一条装配线。调用Add
方法时,它不会立即创建策略,而是收集每个弹性策略的"蓝图"或"指令"。
当最终调用.Build()
时,构建器获取所有这些指令,并开始按正确顺序连接策略,创建我们在第2章讨论的分层"洋葱"结构。链中的每个策略都被赋予对"下一个"策略或实际代码的引用,形成完整的执行路径。
以下是构建器如何组合这些部分的简化视图:
如你所见,构建器
收集设计选择,然后在Build()
步骤中,通过链接策略对象构造实际可用的ResiliencePipeline
。
在幕后,我们使用的AddTimeout
、AddRetry
和其他Add
方法实际上是"扩展方法"。这些方法只是调用构建器上一个更通用的方法AddStrategy
。AddStrategy
方法接受一段特殊代码(“工厂委托”),告诉构建器在.Build()
管道时如何创建特定的弹性策略。
我们甚至可以看到Polly自己的内部逻辑如何使用这种模式。例如,添加自定义策略时,我们会使用AddStrategy
:
using Polly;public static class CustomBuilderExtensions
{// 这是一个简化示例,展示如何用核心'AddStrategy'方法// 实现'AddMyCustomStrategy'public static ResiliencePipelineBuilder AddMyCustomStrategy(this ResiliencePipelineBuilder builder){// 关键是使用'AddStrategy'// 'context'提供有用信息,如遥测,// 策略可能需要这些信息return builder.AddStrategy(context =>{// 这是构建器在调用.Build()时// 实际创建策略对象的地方Console.WriteLine("创建MyCustomStrategy...");return new MyCustomStrategy();});}
}// 然后可以这样使用:
// var pipeline = new ResiliencePipelineBuilder().AddMyCustomStrategy().Build();
这表明ResiliencePipelineBuilder
提供了一种灵活的方式,可以将任何类型的弹性策略添加到管道中,无论是内置的还是自定义的。
结论
弹性管道构建器是我们在Polly中构建强大可靠的**弹性管道**对象的重要工具。
它作为架构师,让我们能够轻松地以正确顺序指定和组合各种"安全规则"(弹性策略对象),而无需手动链接它们。
我们已经学会如何使用new ResiliencePipelineBuilder()
启动构建器,Add
策略,以及.Build()
最终管道。
既然知道如何添加策略,我们可能会想了解配置每个独立策略的所有不同方式。这正是我们下一章要探讨的内容:弹性策略选项。