前言
O(∩_∩)O 大家好!
书接上文,本文将会继续建立在 .Net6 使用 Ocelot + Consul 看这篇就够了 项目的基础上进行Polly的介绍,然后这篇文章只是个人学习与分享,不喜勿喷,谢谢!
什么是Polly?
Polly 是一个 .NET 弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达重试、断路器、超时、隔板隔离、速率限制和回退等策略。
https://github.com/App-vNext/Polly
为什么要用到Polly?
在单体项目中,一个业务流程的执行在一个进程中就完成了,但是在微服务项目中往往会涉及到多个服务甚至多台机器,而这些服务之间一般都是需要使用网络来进行通信,然而网络又是不可靠的,所以往往会因为网络资源,网络连接等问题来影响业务流程的处理,所以在这种情况下我们就需要一些保护机制来保障我们服务的正常运行,而本文介绍的 Polly 就提供了一些很好的应对方案。
在我们正式介绍Polly之前,我们先简单介绍一下一些故障的代名词,网上已经有大量的介绍了,所以这里只会简单介绍,助于理解。
什么是服务雪崩?
在微服务中,服务A调用服务B,服务B可能会调用服务C,服务C又可能调用服务D等等,这种情况非常常见。如果服务D出现不可用或响应时间过长,就会导致服务C原来越多的线程处于网络调用等待状态,进而影响到服务B,再到服务A等,后会耗尽整个系统的资源,导致整体的崩溃,这就是微服务中的“雪崩效应”。
什么服务熔断?
熔断机制就是应对雪崩效应的一种链路保护机制。其实,对于熔断这个词我们并不陌生,在日常生活中经常会接触到,比如:家用电力过载保护器,一旦电压过高(发生漏电等),就会立即断电,有些还会自动重试,以便在电压正常时恢复供电。再比如:股票交易中,如果股票指数过高,也会采用熔断机制,暂停股票的交易。同样,在微服务中,熔断机制就是对超时的服务进行短路,直接返回错误的响应信息,而不再浪费时间去等待不可用的服务,防止故障扩展到整个系统,并在检测到该服务正常时恢复调用链路。
什么是服务降级?
当我们谈到服务熔断时,经常会提到服务降级,它可以看成是熔断器的一部分,因为在熔断器框架中,通常也会包含服务降级功能。
降级的目的是当某个服务提供者发生故障的时候,向调用方返回一个错误响应或者替代响应。从整体负荷来考虑,某个服务熔断后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,这样,虽然服务水平下降,但总比直接挂掉的要好。比如:调用联通接口服务器发送短信失败之后,改用移动短信服务器发送,如果移动短信服务器也失败,则改用电信短信服务器,如果还失败,则返回“失败”响应;再比如:在从推荐商品服务器加载数据的时候,如果失败,则改用从缓存中加载,如果缓存也加载失败,则返回一些本地替代数据。
在某些情况下,我们也会采取主动降级的机制,比如双十一活动等,由于资源的有限,我们也可以把少部分不重要的服务进行降级,以保证重要服务的稳定,待度过难关,再重新开启。
项目准备
.Net 6
Visual Studio 2022
https://github.com/fengzhonghao8-24/.Net6.Polly
Polly的基本使用
//当我们的代码触发HttpRequestException异常时,才进行处理。
Policy.Handle<HttpRequestException>();
//只有触发SqlException异常,并且其异常号为1205的时候才进行处理
Policy.Handle<SqlException>(ex => ex.Number == 1205)
//使用 Or<T> 来实现同时处理多种异常
Policy
.Handle<HttpRequestException>()
.Or<OperationCanceledException>()
//根据返回结果进行故障定义
Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound)
var policy = /*策略定义*/;
var res = await policy.ExecuteAsync(/*业务代码*/);
//指定多个策略
Policy.Wrap(retry, breaker, timeout).ExecuteAsync(/*业务代码*/);
//或者
Policy.Wrap(waitAndRetry.Wrap(breaker)).ExecuteAsync(action);
想了解更多用法请移步官网https://github.com/App-vNext/Polly/wiki
接下来继续介绍几种主要的策略
然后这里说明一下,项目示例都是基于上篇文章的项目,然后在此基础上我们新创建了一个控制台项目PollyConsole用于演示,访问的服务为ServiceA项目。
项目安装Nuget包:
超时策略
Polly 中关于超时的两个策略:一个是悲观策略(Pessimistic),一个是乐观策略(Optimistic)。其中,悲观策略超时后会直接抛异常,而乐观策略则不会,而只是触发CancellationTokenSource.Cancel函数,需要等待委托自行终止操作。一般情况下,我们都会用悲观策略。
代码演示:
我们这里设置了一个超时时间不能超过 5 秒(方便测试),否则就认为是错误的结果的超时策略场景。
悲观策略
执行回调,抛异常
乐观策略
执行效果,不会抛异常
重试策略
请求异常
当发生 HttpRequestException 的时候触发 RetryAsync 重试,并且多重试3次
当前并不存在 /polly/1 api 所以用来模拟重试策略,看看执行效果:
响应异常
当请求结果为 Http Status_Code 500 的时候进行3次重试
我们先在ServiceA服务添加测试接口,然后在PollyConsole项目请求接口,完成测试效果。
ServiceA
PollyConsole
看看执行效果:
降级策略
策略模拟逻辑:
首先我们使用 Policy 的 FallbackAsync("FALLBACK") 方法设置降级的返回值。当我们服务需要降级的时候会返回 "FALLBACK" 的固定值。
同时使用 WrapAsync 方法把重试策略包裹起来。这样我们就可以达到当服务调用失败的时候重试3次,如果重试依然失败那么返回值降级为固定的 "FALLBACK" 值。
我们先正常访问
然后我们停掉被访问的服务 ServiceA
熔断策略与策略包裹(多种策略组合)
首先定义熔断策略
然后定义重试策略与降级策略并进行策略包裹
定义降级方法(模拟)与终测试方法
策略模拟逻辑:
发送请求,观察请求结果,当请求出现异常的时候会进行三次重试,重试后还不行,就会打开断路器10s,10s内不会往服务端发送任何请求,只会请求降级方法,10s后为断路器改为半开状态,会尝试释放部分流量去服务端,再次观察请求结果..........
看看测试效果(当前模拟服务不通的情况):
然后启动被请求的服务,模拟在服务正常情况下的效果:
到这里,我们对于几种策略的演示就差不多大功告成了。如有理解不对的地方还请告知。
本文只是基本的策略演示,实际项目需要结合HttpClientFactory 使用。
结尾
以上都只是我的个人理解,然后也有参考官网以及大佬的文章,文章中如有什么不妥的地方欢迎指正,共同进步。后续有时间还会继续学习相关技术知识,欢迎Star与关注。谢谢😊😁!
参考链接
https://www.cnblogs.com/RainingNight/p/circuitbreaker-polly-in-asp-net-core.html
https://github.com/App-vNext/Polly/wiki