Polly 是 .Net 下的一套瞬时故障处理及恢复的函式库,可让开发者以fluent及线程安全的方式来应用诸如Retry、Circuit Breaker、Timeout、Bulkhead Isolation及Fallback等策略。
安装
可以通过Nuget实现快速安装: Install-Package Polly
基本用法
一个简单的示例如下:
var policy = Policy
.Handle<DivideByZeroException>() //定义所处理的故障
.Retry(); //故障的处理方法
policy.Execute(() => DoSomething()); //应用策略
从上面的例子中我们可以看出,使用该策略一般包括三个步骤:
- 定义所处理的故障
- 定义故障的处理方法
- 应用策略
上述代码在功能上和下面的代码等价:
for (int i = 0; i < 2; i++)
{
try
{
DoSomething();
}
catch (DivideByZeroException)
{
if (i > 1)
throw;
}
}
虽然这个例子比较简单,带来的优越性并不明显,但它以一种比较规范的方式定义了异常的处理策略,一来带来了更好的体验,带来了更好的代码可读性,另外,随着异常策略的复杂,它所带来的对代码的简化就更加明显了。下面就稍微详细一点的深入介绍一下:
定义错误(故障)
常见故障定义方式是指定委托执行过程中出现的特定异常,Polly中支持异常处理方式如下:
// 处理指定异常
Policy.Handle<DivideByZeroException>();
// 处理有条件的指定异常
Policy.Handle<SqlException>(ex => ex.Number == 1205);
// 处理多种异常
Policy.Handle<DivideByZeroException>()
.Or<ArgumentException>();
// 处理多种有条件的异常
Policy.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example");
也支持异常的聚合:
Policy.Handle<ArgumentException>()
.Or<ArgumentException>();
另外,也支持通过返回值判断是否故障:
// 指定错误的返回值
Policy.HandleResult<int>(ret => ret <= 0);
故障处理策略:重试
常见的处理策略是重试,Polly库中内置了各种常用的重试策略:
// 重试1次
Policy.Handle<TimeoutException>().Retry();
// 重试多次
Policy.Handle<TimeoutException>().Retry(3);
// 无限重试
Policy.Handle<TimeoutException>().RetryForever();
Policy.Handle<TimeoutException>().Retry(3, (err, countdown, context) =>
{
// log retry
});
// 等待并重试
Policy.Handle<TimeoutException>().WaitAndRetry(3, _ => TimeSpan.FromSeconds(3));
故障处理策略:回退(Fallback)
Policy.Handle<TimeoutException>().Fallback(() => { });
PS: 带参数的Fallback处理方式貌似在5.0之后发生了变化,成了本文所示的方式,以前是Fallback<T>
故障处理策略:断路保护(Circuit Breaker)
这种策略在调用远程服务时非常实用,当一定时间内的调用都出错时,往往可以认为服务提供者已经不可用,后续调用完全可以直接失败,以避免重试的开销。直到一定时间后才需要再次重试。
相对其它处理策略,CircuitBreaker是一个比较复杂的策略,它是有状态的,可以通过CircuitState属性获取:
var state = circuitBreaker.CircuitState;
- CircuitState.Closed - 常态,可执行actions。
- CircuitState.Open - 自动控制器已断开电路,不允许执行actions。
- CircuitState.HalfOpen - 在自动断路时间到时,从断开的状态复原。可执行actions,后续的action/s或控制的完成,会让状态转至Open或Closed。
- CircuitState.Isolated - 在电路开路的状态时手动hold住,不允许执行actions。
除了超时和策略执行失败的这种自动方式外,也可以手动控制它的状态:
故障封装策略(PolicyWrap)
我们可以通过PolicyWrap的方式,封装出一个更加强大的策略:
这个策略就是将Retry和Fallback组合起来,形成一个retry and fallback的策略,也可以写成如下形式:
retryAndFallback.Execute(DoSomething);
fallback.Execute(()=> retry.Execute(DoSomething));
封装策略本身是属于弹性策略的范畴,这里只是提及一下,以演示Polly模块强大的功能,关于弹性策略更多内容在下文中再做更详细的介绍。