概述
很多朋友包括我,对于“八股文”可以说是比较熟练的,每次面试前都会专研不少东西,各种固定答案、专业术语都是张口就来,一个字,稳。
八股文:程序员八股文是指程序员在面试过程中经常被问到的问题,大多都有固定化、格式化的答案,俗称为面经。
八股文是明清科举考试的一种文体,也称制义、制艺、时文、八比文。
八股文就是指文章的八个部分,文体有固定格式:由破题、承题、起讲、入题、起股、中股、后股、束股八部分组成,题目一律出自四书五经中的原文。
后四个部分每部分有两股排比对偶的文字,合起来共八股。
相信大家对熔断、降级、隔离、限流、容灾备份、故障转移、异地多活等专业术语不陌生吧,或多或少都能聊上一两句。
但是在实际开发中却望着键盘发呆,不知道怎么实现,今天我们就简单演示一下再项目当中怎么实现熔断、降级。
实现的方式有很多,我们这里讲解Polly,但是大家不要过分依赖,可以多种方式进行预演防范。
强如淘宝,在今年(2021年10月20号)20点10分左右都崩了,20点27分左右修复。
熔断和降级概念理解
熔断机制概念
当下游服务因访问压力过大而响应变慢或失败,或者下游服务出现异常时,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。
PS:如果项目中不使用熔断将会影响到整个系统的使用。
举例:
家庭里有很多大功率电器(微波炉,电磁炉,电炒锅,空调,饮水机,电热水器、浴霸、太阳能抽水电机等),
一旦我们全部开启大功能电器,就会造成超负荷运行引起跳闸停电,导致家里面全面停电,这就不符合用电目的,我们想哪个大功能超负荷就停哪个,不能因为局部影响全局。
这个时候我们需要在家庭总电闸下面加装二级电闸、三级电闸.....,达到一机一闸一保护的目的(也可以多个电器安装一个电闸)。
如果在这个例子中,把用电换成我们程序中的接口(接口能承受3000人同时访问),然后把大功率电器替换成我们的接口调用者。
当接口的并发超过3000这个点的时候接口将会挂掉,导致服务器崩溃,作为调用者并不能控制这个点。
为了防止我们的接口发生崩溃的情况,我们做一个报警的设置,当发生这个情况的时候暂时切断服务,防止服务崩溃(用户量到达3000的时候,拒绝其他请求,只服务这3000以内的请求)。
降级机制概念
降级是当某个服务出现故障启动熔断机制后,向调用方返回一个替代响应或者规范化的错误响应。
举例:
我们的程序需要发送邮件,首先调用自建邮箱发送,当自建邮箱发送失败后调用网易邮箱发送邮件,如果网易邮箱发送失败,那么我们就调用 QQ 邮箱发送,如果 QQ 邮箱也发送失败,那么我们就返回发送失败响应。
降级也可以看作服务的选择性放弃。
例如1:上面的多个大功能电器不需要同时运行时,我们可以关闭其他电器,等待当前电器用完后在使用。
例如2:李佳琪和薇娅的直播带货时,平台流量优先倾斜,导致其他主播的直播间卡慢不清晰等问题,等待李佳琪薇娅直播结束后其他主播恢复正常。
例如3:李佳琪和薇娅的直播带货时,平台流量优先倾斜,导致其他主播的直播间卡慢不清晰等问题,等待李佳琪薇娅直播结束后其他主播恢复正常。
当服务接口发生错误的时候,可以找到一个替代方法进行使用。这个方法可能是启用另一台服务器的接口,也可能是只返回“服务器繁忙请重试的提示”。总之不会把一个系统级别的问题暴露给用户。
什么是 Polly
暂时我所了解的处理熔断降级的框架有NetFlix的Hystrix框架和Polly框架。我们这里使用Polly。
Polly 是被 .NET 基金会认可的弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达策略,如重试、断路器、超时、舱壁隔离和回退。
Polly 的7种策略 [1]:重试、断路(熔断)、降级、超时、隔离、缓存以及策略包集合 Polly 的核心是动作和故障,其中动作主要包含降级、熔断和重试等,策略则包含用来执行的代码。
1、重试(Retry):
当程序发生短暂的故障、并且故障在延迟后,可以自动纠正的,前期是暂时的我们可以配置自动重试。
2、断路器(Circuit-Breaker):
当一个系统陷入严重的问题时,让系统快速的失败,比让用户一直等待着要好的多,保护一个系统不受过载的影响,可以帮助它快速的恢复。
3、超时(Timeout):
在等待一定的时间后,没有返回相应的结果,保证程序不会一直等待下去,使调用者不必一直的等待下去。
4、隔离(Bulkhead Isolation):
当进程出现故障的时,一台主机中的多个失败的进程,对资源(例如线程/CPU)的一直占用,当下游的系统发生故障的时候,也可能导致上游对资源的调用失败、这两种风险的出现都将导致更大范围的影响、隔离策略是为了防止“一个点的失败导致整盘的失败”把受到管理的操作固定在某个资源中,避免影响到其他的资源。
5、缓存(Cache):
针对相同的请求,在第一次访问的时候将响应的数据进行缓存,再次访问的时候直接在缓存中提供响应的数据。
6、回退(FallBack):
当程序发生失败的情况的时候,我们将做些什么,定义一个在程序发生失败的时候要执行的动作。
7、策略组合(PolicyWrap):
Polly针对不同的故障有不同的策略,我们可以灵活的组合策略,上述的六种策略可以灵活组合使用。
Polly的基本用法
我们创建一个.NET Core 2.1 控制台应用程序,命名为 PollyDemo。
在Nuget中引用第三方库Polly,选择最新稳定版。
1、重试策略(Retry)
我们定义一个 GetUser 方法,这个方法用来模拟网络请求并在方法结尾制造了一个 Http 异常。
在 Main 方法中我们设定了重试次数 5 次,并且规定了重试过程中要执行的业务逻辑,最后调用 Execute 方法指定针对哪段代码执行这个策略。
我们运行代码可以发现当触发异常后,程序不断的重试调用指定的代码段,直到达到重试次数后不再重试并抛出异常。
我们在使用重试策略需要注意的是,当达到重试次数后代码会抛出异常,因此我们必须在将充实策略代码包裹在 try_catch 代码段中。
using Polly; using System; using System.Net.Http; using System.Threading; namespace PollyDemo { class Program { static void Main(string[] args) { try { //如果程序发生错误将重新执行 5 次 Policy.Handle<HttpRequestException>().Retry(5, ((exception, count, context) => { Console.WriteLine($"执行失败,第 {count} 次重试"); })).Execute(GetUser); } catch (Exception e) { Console.WriteLine("异常抛出:"+e.Message); } Console.Read(); } //开始执行代码块 static void GetUser() { Console.WriteLine("开始执行"); Thread.Sleep(1000); throw new HttpRequestException(); //直接定义错误抛出 } } }
执行结果
2、断路器(Circuit-Breaker)
程序首次被访问如果连续出现三次错误后,将暂停3秒,三秒结束后 可以被继续访问,
继续访问时 如果第一次就出现错误 ,继续熔断10次。
using Polly; using System; using System.Net.Http; using System.Threading; namespace PollyDemo { class Program { static void Main(string[] args) { //连续访问3ci var PolicyExecutes = Policy<object>.Handle<Exception>() .CircuitBreaker(3, TimeSpan.FromSeconds(3)); //模拟访问10次 for (int i = 0; i < 10; i++) { try { PolicyExecutes.Execute(() => { Console.WriteLine("********************开始执行程序**************************"); throw new Exception("异常"); }); } catch (Exception ex) { Console.WriteLine($"Exception信息{i}" + ex.Message); } //一秒运行一次 Thread.Sleep(1000); } Console.Read(); } } }
执行结果
3、超时策略(Timeout)
程序本身没有错误就是执行的时间超过了我们制定的超时策略的时间然后程序就抓捕到超时的异常。
using Polly; using Polly.Timeout; using System; using System.Net.Http; using System.Threading; namespace PollyDemo { class Program { static void Main(string[] args) { try { //超时策略(程序没错,只是执行时间超过设置的时间) Policy policy = Policy.Timeout(3, TimeoutStrategy.Pessimistic); policy.Execute(() => { Console.WriteLine("********************开始执行程序**************************"); Console.WriteLine("等待执行中..."); Thread.Sleep(5000); //设置5s超时 }); } catch (Exception ex) { Console.WriteLine("执行超时,请重试"); } Console.Read(); } } }
执行过程
4、隔离策略(Bulkhead Isolation)
制定策略最大的并发数为5
using Polly; using Polly.Timeout; using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace PollyDemo { class Program { static void Main(string[] args) { Policy PolicyExecutes = Policy.Bulkhead(5); //程序运行6次 总会有一个失败的,但是这个失败的不会影响其他5个的执行结果 for (int i = 0; i < 6; i++) { Task.Factory.StartNew(() => { try { PolicyExecutes.Execute(() => { Console.WriteLine($"********************开始执行程序**************************"); Thread.Sleep(5000); //为了看出效果,设置5s停顿,便于并发执行错误 }); } catch (Exception ex) { Console.WriteLine(ex.Message); } }); } Console.Read(); } } }
执行结果
5、缓存策略(Cache)
在开发时我们会把频繁使用并且很少变化的资源缓存起来,来提高系统的性能。
Polly 提供了强大且易于上手的缓存策略的支持,使得我们不用再手动编写缓存策略。
这里我不提供示例代码,一来我暂时还没使用这个策略,二来这一小部分需要根据不同项目和不同公司的框架来订。
using System; namespace PollyDemo { class Program { static void Main(string[] args) { //Policy.Cache 可以指定 过期时间,绝对过期时间,滑动过期时间等 //Policy 的缓存策略的制定 MemoryCache memoryCache = null; var PolicyExecutes = Policy.Cache(memoryCache, TimeSpan.FromMinutes(5)); Console.Read(); } } }
6、回退(FallBack)
当主程序发生错误是我们启动备用的程序进行程序处理。
using Polly; using System; namespace PollyDemo { class Program { static void Main(string[] args) { var PolicyExecute = Policy.Handle<Exception>().Fallback(() => { Console.WriteLine($"你的程序报错了,我是替代程序!"); }); //执行 PolicyExecute.Execute(() => { Console.WriteLine("开始执行程序"); Console.WriteLine(""); throw new Exception(); }); Console.Read(); } } }
执行结果
7、策略组合(PolicyWrap)
PolicyWrap的上面六种策略可以任意的组合起来使用:我们将超时策略(Timeout)加上回退(FallBack)策略组合使用。
using Polly; using Polly.Timeout; using System; using System.Threading; namespace PollyDemo { class Program { static void Main(string[] args) { Policy PolicyExecute = Policy.Handle<Exception>().Fallback(() => { Console.WriteLine("你的执行程序超时了,我是替代程序!"); }); //超时策略 PolicyExecute = PolicyExecute.Wrap(Policy.Timeout(3, TimeoutStrategy.Pessimistic)); //执行 PolicyExecute.Execute(() => { Console.WriteLine($"我是一开始的执行程序"); Console.WriteLine(""); Thread.Sleep(5000); }); Console.Read(); } } }
执行结果
总结
本文开头主要介绍了熔断和降级的相关概念,然后用讲解了 Polly 的其中策略,这些都可以作为掌握 Polly 的基础。
因为 Polly 是一个强大而丰富的 .NET 第三方库,无法通过一篇文章具体而又详细的讲解出来,因此对于 Polly 和 熔断以及降级有兴趣的同学可以上 Polly 官方文档中具体学习。
参考文献
1.Polly的7种策略:https://blog.csdn.net/sd7o95o/article/details/117003880