GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
【 如果你想靠AI翻身,你先需要一个靠谱的工具! 】
Polly 是 .Net 下的一套瞬时故障处理及恢复的函式库,可让开发者以fluent及线程安全的方式来应用诸如Retry、Circuit Breaker、Timeout、Bulkhead Isolation及Fallback等策略。
可以通过Nuget实现快速安装:Install-Package Polly
一个简单的示例如下:
1 2 3 4 5 | var policy = Policy .Handle<DivideByZeroException>() //定义所处理的故障 .Retry(); //故障的处理方法 policy.Execute(() => DoSomething()); //应用策略 |
从上面的例子中我们可以看出,使用该策略一般包括三个步骤:
上述代码在功能上和下面的代码等价:
1 2 3 4 5 6 7 8 9 10 11 12 | for ( int i = 0; i < 2; i++) { try { DoSomething(); } catch (DivideByZeroException) { if (i > 1) throw ; } } |
虽然这个例子比较简单,带来的优越性并不明显,但它以一种比较规范的方式定义了异常的处理策略,一来带来了更好的体验,带来了更好的代码可读性,另外,随着异常策略的复杂,它所带来的对代码的简化就更加明显了。下面就稍微详细一点的深入介绍一下:
常见故障定义方式是指定委托执行过程中出现的特定异常,Polly中支持异常处理方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 处理指定异常 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" ); |
也支持异常的聚合:
1 2 | Policy.Handle<ArgumentException>() .Or<ArgumentException>(); |
另外,也支持通过返回值判断是否故障:
1 2 | // 指定错误的返回值 Policy.HandleResult< int >(ret => ret <= 0); |
常见的处理策略是重试,Polly库中内置了各种常用的重试策略:
1 2 3 4 5 6 7 8 | // 重试1次 Policy.Handle<TimeoutException>().Retry(); // 重试多次 Policy.Handle<TimeoutException>().Retry(3); // 无限重试 Policy.Handle<TimeoutException>().RetryForever(); |
也支持retry时增加一些额外的行为:
1 2 3 4 | Policy.Handle<TimeoutException>().Retry(3, (err, countdown, context) => { // log retry }); |
也支持等待并重试:
1 2 | // 等待并重试 Policy.Handle<TimeoutException>().WaitAndRetry(3, _ => TimeSpan.FromSeconds(3)); |
Fallback策略是在遇到故障是指定一个默认的返回值,
1 2 | Policy< int >.Handle<TimeoutException>().Fallback(3); Policy< int >.Handle<TimeoutException>().Fallback(() => 3); |
当然,遇到没有返回值的也可以指定故障时的处理方法,
1 | Policy.Handle<TimeoutException>().Fallback(() => { }); |
使用Fallback时,异常被捕获,返回默认的返回结果。
PS: 带参数的Fallback处理方式貌似在5.0之后发生了变化,成了本文所示的方式,以前是Fallback<T>
Circuit Breaker也是一种比较常见的处理策略,它可以指定一定时间内最大的故障发生次数,当超过了该故障次数时,在该时间段内,不再执行Policy内的委托。下面以一个简单的示例演示下该策略的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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(); } |
执行结果如下:
1 2 3 4 5 6 7 8 | 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属性获取:
1 | var state = circuitBreaker.CircuitState; |
它有四种状态:
除了超时和策略执行失败的这种自动方式外,也可以手动控制它的状态:
1 2 3 4 5 | // 手动打开(且保持)一个断路器–例如手动隔离downstream的服务 circuitBreaker.Isolate(); // 重置一个断路器回closed的状态,可再次接受actions的执行 circuitBreaker.Reset(); |
更多的介绍可以参看官方文档:Circuit Breaker
我们可以通过PolicyWrap的方式,封装出一个更加强大的策略:
1 2 3 4 | 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的策略,也可以写成如下形式:
1 | Policy.Wrap(fallback, retry); |
当执行这个新策略时:
1 | retryAndFallback.Execute(DoSomething); |
等价于执行:
1 | fallback.Execute(()=> retry.Execute(DoSomething)); |
封装策略本身是属于弹性策略的范畴,这里只是提及一下,以演示Polly模块强大的功能,关于弹性策略更多内容在下文中再做更详细的介绍。
到此这篇关于.Net弹性和瞬态故障处理库Polly的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。