[Polly智能维护网络] 网络重试原理 | 弹性策略
链接:https://github.com/App-vNext/Polly/wiki/Asynchronous-action-execution
docs:Polly
Polly通过自动
处理临时网络问题或响应
缓慢等问题,帮助我们的代码变得更健壮和可靠。
它让我们能够定义智能规则
(如"重试"或"稍等片刻"),这些规则通过中央管道
应用,确保即使在出现问题时应用程序也能平稳处理。
可视化
章节内容
- 弹性策略
- 弹性管道
- 弹性管道构建器
- 弹性策略选项
- 结果
- 谓词构建器
- 弹性上下文
重试策略说明
语法
RetryPolicy retry = Policy.Handle<HttpRequestException>().Retry(3);
上述示例将创建一个重试策略
,当操作因策略处理的异常而失败时,最多重试三次。
完整重试语法和重载(包括无限重试
、等待后重试
及相关变体)请参见:https://github.com/App-vNext/Polly/tree/7.2.4#retry
给出的语法示例是同步版本;异步操作有对应的异步重载
:请参阅wiki。
Polly重试工作原理
重试操作流程
当通过策略执行一个操作时:
- 重试策略尝试执行
.Execute(...)
(或类似)委托中的操作 - 如果操作执行成功:
- 返回相关值(如果有)
- 策略退出
- 如果操作抛出未处理的异常:
- 重新抛出异常
- 策略退出(不再尝试)
- 如果操作抛出已处理的异常(或处理故障时返回已处理的故障结果):
- 统计异常/故障
- 检查是否允许再次重试:
- 如果不允许:重新抛出异常/返回故障结果,策略终止
- 如果
允许
:- 对于等待后重试策略:根据配置计算
等待
时长 触发
onRetry委托(如配置)- 对于等待后重试策略:等待计算出的时长
- 返回流程起点,再次尝试执行操作
- 对于等待后重试策略:根据配置计算
总尝试次数
总尝试次数 = 1(初始尝试) + 配置的重试次数
。
例如配置.Retry(3)
时,最多尝试4次(初始1次+最多3次重试)。
可配置策略委托
onRetry / onRetryAsync
可以在许多策略上配置可选的onRetry或onRetryAsync委托。委托的输入参数可能包括部分或全部:
-
retryCount(重试计数):
- 1:第一次尝试后,第一次重试前
- 2:第一次重试后,第二次重试前(以此类推)
-
Exception或DelegateResult:
- Exception:触发重试的异常(仅处理异常的策略)
- 或:
- DelegateResult:触发重试的前次执行结果(处理异常和/或结果的策略):
- DelegateResult.Exception:触发重试的异常(如因异常触发);否则null
- DelegateResult.Result:触发重试的处理结果值(如因结果触发);否则default(TResult)
-
TimeSpan:
- WaitAndRetry策略中等待下次重试的时长(onRetry/Async委托在等待开始前调用)
-
Context:
- 随此次执行通过策略传递的唯一执行上下文
sleepDurationProvider
某些WaitAndRetry策略接受sleepDurationProvider函数来提供特定重试尝试的等待时长。
所有版本的sleepDurationProvider都有一个返回参数TimeSpan(下次重试前的等待时间)。
委托的输入参数可能包括部分或全部:
- retryCount:同上
- Context:同上
- Exception或DelegateResult:同上
第1章:弹性策略
想象我们正在构建一个应用程序,可能是移动应用或网站。
这个应用经常需要与其他服务通信
或从互联网获取信息。如果网络连接缓慢会怎样?或者如果其他服务暂时不可用呢?我们的应用可能会冻结、显示错误甚至崩溃!
为了让应用程序更健壮可靠,我们需要一些"安全规则"来告诉代码在出现问题时该怎么做
。
这些安全规则帮助应用程序优雅地处理常见问题,如网络故障、响应缓慢或临时错误,使其更具"弹性"。
在Polly中,这些单独的"安全规则"被称为弹性策略。
什么是弹性策略?
将弹性策略视为一个大型"战术手册"中的单个专门化"安全规则"。每个策略都设计用于处理*特定类型*的问题
。
例如:
- 重试策略:这条规则表示"如果操作失败(可能是由于临时网络波动),不要放弃!再试几次。"
- 超时策略:这条规则表示"如果操作耗时过长,停止等待!我们
不能永远挂起
。" - 断路器策略:这条规则就像房屋中的保险丝。如果服务持续失败,它会"跳闸"并暂时阻止进一步调用,
给服务恢复的时间
。这可以防止应用程序继续冲击已经出现问题的服务。
这些策略是基础构建块。我们可以组合它们来创建应用程序应对问题的全面计划。
如何使用弹性策略
让我们看一个简单例子:确保操作不会耗时过长。为此,我们将使用Timeout
策略。
首先,告诉Polly我们想使用Timeout
策略。通常在为代码设置"战术手册"时这样做(Polly称之为弹性管道,后续会详细介绍)。
以下是添加Timeout
策略的方式:
using Polly;
using Polly.Timeout; // 需要TimeoutRejectedException// 我们正在构建一个'弹性管道'来保存策略。
// 现在可以将其视为安全规则的容器。
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()// 添加超时策略:如果操作超过5秒,停止它!.AddTimeout(TimeSpan.FromSeconds(5)).Build();
在这段代码中,我们创建了一个pipeline
,其中只包含一个弹性策略:设置为5秒的Timeout
策略。
该策略确保通过此pipeline
运行的任何代码都将被限制在5秒内。
现在,让我们使用这个策略执行一些代码:
try
{// 通过'pipeline'执行操作await pipeline.ExecuteAsync(async token =>{Console.WriteLine("尝试执行某些操作...");await Task.Delay(TimeSpan.FromSeconds(6), token); // 故意耗时过长!Console.WriteLine("操作完成!"); // 这行不会执行}, CancellationToken.None);
}
catch (TimeoutRejectedException)
{Console.WriteLine("操作超时!超时策略生效了!");
}
这里发生了什么?
- 告诉包含
Timeout
策略的pipeline
执行特定任务(await Task.Delay
6秒的部分)。 Timeout
策略启动计时器。- 由于任务耗时6秒,而
Timeout
策略设置为5秒,策略介入! - 它停止任务并抛出
TimeoutRejectedException
。 catch
块处理这个异常,看到"操作超时!超时策略生效了!"被打印出来。
这展示了单个弹性策略如何作为安全规则,对我们想要执行的操作强制执行特定行为(本例中是时间限制)。
另一个常见策略是AddRetry
:
using Polly;
using Polly.Retry;ResiliencePipeline retryPipeline = new ResiliencePipelineBuilder()// 添加重试策略:如果发生错误,最多重试3次。.AddRetry(new RetryStrategyOptions{MaxRetryAttempts = 3,Delay = TimeSpan.FromSeconds(1) // 每次重试间隔1秒}).Build();// 然后以类似方式通过retryPipeline执行代码。
弹性策略的内部工作原理
本质上,弹性策略通过用特定逻辑包装代码来工作。
想象我们将任务交给策略,策略在运行前添加其"安全包装"。
以下是Timeout
等策略工作的简化序列:
在图中:
- 我们的代码告诉弹性策略运行任务。
- 弹性策略启动其内部逻辑(如
Timeout
的计时器)。 - 然后,弹性策略运行实际操作。
- 实际操作完成并向弹性策略返回结果或错误。
- 弹性策略应用其规则(如检查操作完成前计时器是否到期)。
- 最后,根据其规则,弹性策略向我们的代码返回最终结果(或抛出异常,如超时)。
Polly通过名为ResilienceStrategy
的基类实现这一点。所有特定策略如RetryStrategy
或TimeoutStrategy
都继承自该基类,并实现名为ExecuteCore
的特殊方法。这个ExecuteCore
方法就是策略独特"安全规则"逻辑所在之处。
让我们看看TimingResilienceStrategy
(类似于Timeout
)内部结构的简化版本,如Polly源代码中所示(来自samples/Extensibility/Proactive/TimingResilienceStrategy.cs
):
using Polly; // 提供ResilienceStrategy基类
using System.Diagnostics; // 测量时间internal sealed class TimingResilienceStrategy : ResilienceStrategy
{private readonly TimeSpan _threshold; // 此策略的时间限制public TimingResilienceStrategy(TimeSpan threshold){_threshold = threshold;}// 策略"规则"的应用之处!protected override async ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>(Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback, // 这是图中的'我们的任务'ResilienceContext context,TState state){var stopwatch = Stopwatch.StartNew(); // 启动计时器!// 步骤1:执行应用程序提供的原始操作。// 'callback'就是我们想要保护的代码。Outcome<TResult> outcome = await callback(context, state);// 步骤2:应用策略的特定规则(如检查耗时)。if (stopwatch.Elapsed > _threshold){// 规则被打破!对于真正的超时策略,// 这会抛出TimeoutRejectedException。// 对于TimingResilienceStrategy,可能只是报告警告。}// 步骤3:返回操作结果。return outcome;}
}
如你所见,ExecuteCore
方法接收callback
(我们想要运行的操作),执行它,然后应用其特定规则(if (stopwatch.Elapsed > _threshold)
)。这是所有弹性策略的核心模式。
🎢常见弹性策略
Polly提供许多内置弹性策略,每种处理不同类型的问题:
策略名称 | 解决的问题 | 类比 |
---|---|---|
重试 | 临时故障,瞬时错误 | “如果一开始没有成功,再试一次!” |
超时 | 操作耗时过长 | “不要浪费时间永远等待!” |
断路器 | 依赖项持续失败 | “如果事情持续出错,休息一下 。” |
回退 | 操作失败时提供默认响应 | “如果计划A失败,使用计划B!” |
速率限制 | 对服务的过多请求 | “不要淹没系统。” |
舱壁 | 一个故障依赖影响其他部分 | “分而治之(隔离故障 )。” |
对冲 | 响应缓慢,并行尝试多次 | “派出两个信使,总有一个 会到达!” |
混沌 | 故意注入故障以测试弹性 | “打破它以使其更强。” |
每种策略都是应用程序战术手册中的单独"安全规则"。
结论
-
Polly是一个.NET弹性库,通过智能策略帮助处理
网络故障
和响应缓慢
等问题。 -
它提供重试、超时等策略,可组合使用增强应用健壮性。文档详细介绍了弹性策略、管道、构建器等核心概念,并给出
重试策略的语法示例和工作原理
说明。
在本章中,我们了解到弹性策略是设计用于处理应用程序中特定问题的单独安全规则,如重试失败操作或防止长时间等待。它们是使应用程序更健壮的基本构建块。
但是如何将这些单独的安全规则组合成应用程序完整有序的"战术手册"呢?这正是我们将在下一章探讨的内容:弹性管道。