donet 微服务开发 学习-熔断降级
目的介绍
donet 微服务开发 学习
什么是熔断降级
熔断器如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费时间去等到长时间的超时产生。
降级的目的是当某个服务提供者发生故障的时候,向调用方返回一个错误响应或者替代响应。举例子:如视频播放器请求playsafe的替代方案;加载内容评论时如果出错,则以缓存中加载或者显示"评论暂时不可用" 。
Polly简介
.Net Core中有一个被.Net基金会认可的库Polly,可以用来简化熔断降级的处理。主要功能:重试(Retry) ;断路器(Circuit-breaker) ;超时检测(Timeout) ;缓存(Cache) ;,失败处理(FallBack) ;
官网: https://github. com/App-vNext/Polly
介绍文章: https://www.cnblogs.com/CreateMyself/p/7589397.html
Polly简单使用
使用Policv的静态方法创建ISyncPolicy实现类对象,创建方法既有同步方法也有异步方法,根 据自己的需要选择。下面演示同步的,异步的用法类似。
举例:当发生ArgumentException异常的时候,执行Fallback代码。
新建pollytest1控制台项目,添加nuget引用
try
{
ISyncPolicy policy = Policy.Handle<ArgumentException>(ex => ex.Message == "年龄参数错误")
.Fallback(() =>
{
Console.WriteLine("出错了");
});
policy.Execute(()=>{
//这里是可能会产生问题的业务系统代码
Console.WriteLine("开始任务");
throw new ArgumentException("年龄参数错误");
//throw new Exception("haha");
//Console.WriteLine("完成任务");
});
}
catch (Exception ex)
{
Console.WriteLine($"未处理异常:{ex}");
}
如果没有被Handle处理的异常,则会导致未处理异常被抛出。
详解Polly异常处理
. Handle<Exception> (ex->ex. Message. Contains ("aa"))
参数委托的返回值是boolean类型,如果返回true,就是“这个异常能被我处理”,否则就是“我处理不了" ,会导致未处理异常被抛出。
比如可以实现“我能处理XXX错误信息"
Handle<WebException> (ex=>ex. Status==WebExceptionStatus. SendFailure)
获取异常信息就调用这个重载
public static FallbackPolicy Fallback(this PolicyBuilder policyBuilder, Action fallbackAction, Action<Exception> onFallback);
//省略
.Fallback(() =>{},(ex)=> {
Console.WriteLine("执行出错,异常"+ex);
});
异常处理的套路
ISyncPolicy policy = Policy.Handle<AException>()
.Or<BException>()
.Or<CException>()
......
.
CircuitBreaker()/.Fallback()/.Retry()/.RetryForever()/.WaitAndRetry()/.WaitAndRetryForever()
当发生AException或者BException或者…的时候进行CircuitBreaker()/.Fallback()等处理。
这些处理不能简单的链式调用,要用到后面的Wrap。
例如下面这样是不行的
ISyncPolicy policy = Policy
.Handle<Exception>()
.Retry(3)
.Fallback(()=> { Console.WriteLine("执行出错"); });//这样不行
policy.Execute(() => {
Console.WriteLine("开始任务");
throw new ArgumentException("Hello world!");
Console.WriteLine("完成任务");
});
重试处理
try
{
ISyncPolicy policy = Policy.Handle<Exception>()
.RetryForever();//一直重试
policy.Execute(() =>
{
Console.WriteLine("开始任务");
if (DateTime.Now.Second % 10 != 0)
{
throw new Exception("出错");
}
Console.WriteLine("完成任务");
});
}
catch (Exception ex)
{
Console.WriteLine($"未处理异常:{ex}");
}
//RetryForever()是一直重试直到成功
//Retry()是重试最多一次;
//Retry(n)是重试最多n次;
//WaitAndRetry()可以实现“如果出错等待100ms再试还不行再等150ms秒。。。。”,重载方法很多,一看就懂,不再一一介绍。还有WaitAndRetryForever。
短路保护Circuit Breaker
出现N次连续错误,则把“熔断器”(保险丝)熔断,等待一段时间,等待这段时间内如果再Execute则直接抛出BrokenCircuitException异常。等待时间过去之后,再执行Execute的时候如果又错了(一次就够了),那么继续熔断一段时间,否则就回复正常。
这样就避免一个服务已经不可用了,还是使劲的请求给系统造成更大压力。
ISyncPolicy policy = Policy.Handle<Exception>()
.CircuitBreaker(6, TimeSpan.FromSeconds(5));//连续出错6次之后熔断5秒(不会再去尝试执行业务代码)。
while (true)
{
Console.WriteLine("开始Execute");
try
{
policy.Execute(() =>
{
Console.WriteLine("开始任务");
throw new Exception("出错");
Console.WriteLine("完成任务");
});
}
catch (Exception ex)
{
Console.WriteLine("execute出错" + ex.GetType() + ":" + ex.Message);
}
Thread.Sleep(500);
}
策略封装
可以把多个ISyncPolicy合并到一起执行:
policy3= policy1.Wrap(policy2);
执行policy3就会把policy1、policy2封装到一起执行
policy9=Policy.Wrap(policy1, policy2, policy3, policy4, policy5);把更多一起封装。
超时处理
创建一个3秒钟(注意单位)的超时策略。
ISyncPolicy policy = Policy.Timeout(3, TimeoutStrategy.Pessimistic);
创建一个3秒钟(注意单位)的超时策略。超时策略一般不能直接用,而是和其他封装到一起用:
ISyncPolicy policy = Policy.Handle<Exception>()
.Fallback(() =>
{
Console.WriteLine("执行出错");
});
policy = policy.Wrap(Policy.Timeout(2, TimeoutStrategy.Pessimistic));
policy.Execute(() =>
{
Console.WriteLine("开始任务");
Thread.Sleep(5000);
Console.WriteLine("完成任务");
});
上面的代码就是如果执行超过2秒钟,则直接Fallback,Execute中的代码也会被强行终止(引发TimeoutRejectedException异常)。
这个的用途:请求网络接口,避免接口长期没有响应造成系统卡死。
TimeoutStrategy.Optimistic是主动通知代码,告诉他“到期了”,由代码自己决定是不是继续执行,局限性很大,一般不用。
下面的代码,如果发生超时,重试最多3次(也就是说一共执行4次哦)。
ISyncPolicy policy = Policy.Handle<TimeoutRejectedException>()
.Retry(1);
policy = policy.Wrap(Policy.Timeout(3, TimeoutStrategy.Pessimistic));
policy.Execute(() =>
{
Console.WriteLine("开始任务");
Thread.Sleep(5000);
Console.WriteLine("完成任务");
});
缓存
缓存的意思就是N秒内只调用一次方法,其他的调用都返回缓存的数据。
目前只支持Polly 5.9.0,不支持最新版
Install-Package Polly.Caching.MemoryCache
功能局限性也大,简单讲一下,后续先不用这个实现缓存原则:别人的好用我就拿来用,不好用我就自己造。
命令空间都写到代码中,因为有容易引起混淆的同名类。
//Install-Package Microsoft.Extensions.Caching.Memory
Microsoft.Extensions.Caching.Memory.IMemoryCache memoryCache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions());
//Install-Package Polly.Caching.MemoryCache
Polly.Caching.MemoryCache.MemoryCacheProvider memoryCacheProvider = new Polly.Caching.MemoryCache.MemoryCacheProvider(memoryCache);
CachePolicy policy = Policy.Cache(memoryCacheProvider, TimeSpan.FromSeconds(5));
Random rand = new Random();
while (true)
{
int i = rand.Next(5);
Console.WriteLine("产生"+i);
var context = new Context("doublecache" + i);
int result = policy.Execute(ctx =>
{
Console.WriteLine("Execute计算"+i);
return i * 2;
},context);
Console.WriteLine("计算结果:"+result);
Thread.Sleep(500);
}