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 ();
也支持retry时增加一些额外的行为:
Policy
.
Handle
<
TimeoutException
>().
Retry
(3, (err, countdown, context) =>
{
// log retry
});
也支持等待并重试:
//
等待并重试
Policy
.
Handle
<
TimeoutException
>().
WaitAndRetry
(3, _ =>
TimeSpan
.
FromSeconds (3));
处理策略:回退(Fallback)
Fallback策略是在遇到故障是指定一个默认的返回值,
Policy
<
int
>.
Handle
<
TimeoutException
>().
Fallback
(3);
Policy
<
int
>.
Handle
<
TimeoutException
>().
Fallback (() => 3);
当然,遇到没有返回值的也可以指定故障时的处理方法,
Policy . Handle < TimeoutException >(). Fallback (() => { });
使用Fallback时,异常被捕获,返回默认的返回结果。
PS: 带参数的Fallback处理方式貌似在5.0之后发生了变化,成了本文所示的方式,以前是Fallback<T>
处理策略:断路保护(Circuit Breaker)
Circuit Breaker也是一种比较常见的处理策略,它可以指定一定时间内最大的故障发生次数,当超过了该故障次数时,在该时间段内,不再执行Policy内的委托。下面以一个简单的示例演示下该策略的功能:
static
void
testPolicy
()
{
var
circuitBreaker =
Policy
.
Handle
<
TimeoutException
>()
.
CircuitBreaker
(3,
TimeSpan
.
FromMinutes
(1));
for
(
int
i
= 0; i
< 5; i
++)
{
try
{
circuitBreaker.
Execute
(
DoSomething
);
}
catch
(Polly.CircuitBreaker.
BrokenCircuitException
e)
{
Console
.
WriteLine
(e.
Message
);
}
catch
(
TimeoutException
)
{
Console
.
WriteLine
(
"timeout"
);
}
}
}
static
int
index
= 0;
static
int
DoSomething
()
{
Console
.
WriteLine
(
$
"DoSomething
{
index
++}
"
);
throw
new
TimeoutException
();
}
执行结果如下:
DoSomething 0
timeout
DoSomething 1
timeout
DoSomething 2
timeout
The circuit is now open and is not allowing calls.
The circuit is now open and is not allowing calls.
可以看到,前面3次都能执行委托DoSomething,但出错次数到达3次后,已经进入断路保护章台,后面两次调用直接返回BrokenCircuitException。直到达到保护时间超时后,对策略的调用才会再次执行DoSomething委托。
这种策略在调用远程服务时非常实用,当一定时间内的调用都出错时,往往可以认为服务提供者已经不可用,后续调用完全可以直接失败,以避免重试的开销。直到一定时间后才需要再次重试。
相对其它处理策略,CircuitBreaker是一个比较复杂的策略,它是有状态的,可以通过CircuitState属性获取:
var state = circuitBreaker. CircuitState ;
它有四种状态:
- CircuitState.Closed - 常态,可执行actions。
- CircuitState.Open - 自动控制器已断开电路,不允许执行actions。
- CircuitState.HalfOpen - 在自动断路时间到时,从断开的状态复原。可执行actions,接续的action/s或控制的完成,会让状态转至Open或Closed。
- CircuitState.Isolated - 在电路开路的状态时手动hold住,不允许执行actions。
除了超时和策略执行失败的这种自动方式外,也可以手动控制它的状态:
//
手动打开
(
且保持
)
一个断路器
–
例如手动隔离
downstream
的服务
circuitBreaker.
Isolate
();
//
重置一个断路器回
closed
的状态,可再次接受
actions
的执行
circuitBreaker.
Reset ();
更多的介绍可以参看官方文档: Circuit Breaker
策略封装(PolicyWrap)
我们可以通过PolicyWrap的方式,封装出一个更加强大的策略:
var
fallback =
Policy
<
int
>.
Handle
<
TimeoutException
>().
Fallback
(100);
var
retry =
Policy
<
int
>.
Handle
<
TimeoutException
>().
Retry
(2);
var
retryAndFallback = fallback.
Wrap (retry);
这个策略就是将Retry和Fallback组合起来,形成一个retry and fallback的策略,也可以写成如下形式:
Policy . Wrap (fallback, retry);
当执行这个新策略时:
retryAndFallback . Execute ( DoSomething );
等价于执行:
fallback. Execute (()=> retry. Execute ( DoSomething ));
策略上下文
在策略的处理过程中,有一个上下文对象,可以在回调函数中使用:
public static RetryPolicy Retry ( this PolicyBuilder policyBuilder, int retryCount, Action < Exception , int , Context > onRetry);
它是一个 IDictionary < string , object > 类型 的对象,它在Policy的执行过程中都可以使用,如:
Policy
.
Handle
<
TimeoutException
>().
Retry
(3, (err, countDown, context) =>
{
var
method = context[
"method"
];
ConsoleLogger
.
WriteLine
(method);
})
这个上下文对象可以在应用策略的时候带入:
policy.
Execute
(
DoSomething
,
new
Context
(
"context"
)
{
[
"method"
] =
"PolicyTest"
});
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。