.Net Core with 微服务 - Polly 服务降级熔断

在我们实施微服务之后,服务间的调用变的异常频繁。多个服务之间可能是互相依赖的关系。某个服务出现故障或者是服务间的网络出现故障都会造成服务调用的失败,进而影响到某个业务服务处理失败。某一个服务调用失败轻则造成当前相关业务无法处理;重则可能耗尽资源而拉垮整个应用。为了尽可能的保证我们生产环境的可用性,至少是部分可用性我们就需要一些策略来保护我们的服务。

服务降级

比如我们的订单详情服务里面会调用会员信息服务接口。如果会员信息服务接口故障会造成订单详情服务也同样故障。这时候我们可以对会员信息服务接口进行降级,在发生故障的时候直接返回固定的信息从而保证订单详情主服务是可用的。
另外一种情况是服务器的资源总是有限的,在面对突发的高并发,高流量情况下我们也可以对部分服务进行降级处理,从而释放更多的资源给核心服务,从而保证核心业务正常工作。

熔断

我们的服务很可能是一个链式的调用的过程。期间如果某个服务出现故障,特别是出现超时故障的时候很有可能耗尽服务器的资源从而影响整个服务。比如订单详情服务依赖会员信息服务,如果会员信息服务因为某些原因出现处理过慢、异常等情况,会阻塞整个订单详情服务的链路。而可能其它服务同样依赖订单详情服务,这样其它服务同样也会被阻塞。资源被越来越多的消耗而不释放,造成所有服务处理越来越慢,积压的请求越来越多, 犹如死循环一般,直到所有资源都被耗尽,整个生成环境奔溃。
所以面对这种情况当我们某个服务持续出现故障的时候我们可以直接断开对它的调用依赖,从而保证不会因为请求积压造成资源耗尽的情况发生。

Polly

Polly 是一个开源的弹性跟瞬态故障处理类库。它可以在你的程序出现故障,超时,或者返回值达成某种条件的时候进行多种策略处理,比如重试、降级、熔断等等。它是 .NET Foundation 的成员项目。

Policy.Handle< T >

Policy.Handle< T > 用来定义异常的类型,表示当执行的方法发生某种异常的时候定义为故障。
当故障发生的时候 Polly 会为我们自动执行某种恢复策略,比如重试。
我们演示项目中,订单接口需要获取会员的详细信息。
http 有一定几率失败,下面我们演示下如果使用 Polly 在出现当请求网络失败的时候进行3次重试。

var memberJson = await Policy.Handle<HttpRequestException>().RetryAsync(3).ExecuteAsync(async () =>{using (var httpClient = new HttpClient()){httpClient.BaseAddress = new Uri($"http://{memberServiceAddress.Address}:{memberServiceAddress.Port}");var memberResult = await httpClient.GetAsync("/member/" + order.MemberId);memberResult.EnsureSuccessStatusCode();var json = await memberResult.Content.ReadAsStringAsync();return json;}});

使用 Policy.Handle< HttpRequestException > 来捕获网络异常。当发生 HttpRequestException 的时候触发 RetryAsync 重试,并且最多重试3次。以下我们接着演示下当 http 的返回值是500的时候进行3次重试:

Policy.HandleResult< T>

Policy.HandleResult< T > 用来定义返回值的类型,表示当执行的方法返回值达成某种条件的时候定义为故障。
当故障发生的时候 Polly 会为我们自动执行某种恢复策略,比如重试。
下面我们演示下如何使用 Polly 在出现当请求结果为 http status_code 500 的时候进行3次重试。

  var memberResult = await Policy.HandleResult<HttpResponseMessage>(x => (int)x.StatusCode == 500).RetryAsync(3).ExecuteAsync(async () =>{using (var httpClient = new HttpClient()){httpClient.BaseAddress =new Uri($"http://{memberServiceAddress.Address}:{memberServiceAddress.Port}");var result = await httpClient.GetAsync("/member/" + order.MemberId);return result;}});

Policy.TimeoutAsync

Policy.TimeoutAsync 表示当一个操作超过设定时间时会引发一个 TimeoutRejectedException 。
这也是一个很常用的故障处理策略。

var memberJson = await Policy.TimeoutAsync(10).ExecuteAsync(async () =>{using (var httpClient = new HttpClient()){httpClient.BaseAddress =new Uri($"http://{memberServiceAddress.Address}:{memberServiceAddress.Port}");var memberResult = await httpClient.GetAsync("/member/" + order.MemberId);memberResult.EnsureSuccessStatusCode();var json = await memberResult.Content.ReadAsStringAsync();return json;}});

以上代码表示当获取会员详情的接口超过10秒还未返回结果的时候直接抛出一个 TimeoutRejectedException 异常终止执行。

服务降级

以上我们演示了出现故障的时候如何进行重试,但是所有重试都失败我们的程序还是会故障。
因为期间某个服务持续的故障导致更多的服务出现故障,一系列连锁反应后很可能导致整个应用瘫痪。
面对这种情况我们可以把相关服务进行降级。
当相关服务调用失败的时候我们可以给出一个统一标准的失败返回值,而不是直接抛出异常。让我们的程序依然能够继续执行下去。
下面我们演示下如何使用 Polly 进行服务调用的降级处理。

 var fallback = Policy<string>.Handle<HttpRequestException>().FallbackAsync("FALLBACK").WrapAsync(Policy.Handle<HttpRequestException>().RetryAsync(3));var memberJson = await fallback.ExecuteAsync(async () =>{using (var httpClient = new HttpClient()){httpClient.BaseAddress =new Uri($"http://{memberServiceAddress.Address}:{memberServiceAddress.Port}");var result = await httpClient.GetAsync("/member/" + order.MemberId);result.EnsureSuccessStatusCode();var json = await result.Content.ReadAsStringAsync();return json;}});if (memberJson != "FALLBACK"){var member = JsonConvert.DeserializeObject<MemberVM>(memberJson);vm.Member = member;}

首先我们使用 Policy 的 FallbackAsync("FALLBACK") 方法设置降级的返回值。当我们服务需要降级的时候会返回 "FALLBACK" 的固定值。
同时使用 WrapAsync 方法把重试策略包裹起来。这样我们就可以达到当服务调用失败的时候重试3次,如果重试依然失败那么返回值降级为固定的 "FALLBACK" 值。

熔断

通过以上演示,我们的服务当发生故障的时候可以自动重试,自动降级了。虽然现在看起来挺健壮,但是还是会有不小的问题。
当我们引入重试策略后,如果服务调用一直失败,每次调用都会反复进行重试,虽然最后会进行降级处理,但是这势必会影响服务的处理速度。
当流量很大的时候,某个接口服务调用很慢有可能会阻塞整个服务,请求不断积压,资源不断耗尽,速度越来越慢,这是一种恶性循环。最终同样可能导致整个应用全部瘫痪的严重后果。
面对这种情况我们就需要引入熔断机制。当一个服务的调用频繁出现故障的时候我们可以认为它当前是不稳定的,在一段时间内我们不应该再去调用这个服务。

        static AsyncCircuitBreakerPolicy circuitBreaker =  Policy.Handle<HttpRequestException>().CircuitBreakerAsync(exceptionsAllowedBeforeBreaking: 10,durationOfBreak: TimeSpan.FromSeconds(30),onBreak: (ex, ts) =>{Console.WriteLine("circuitBreaker onBreak .");},onReset: () =>{Console.WriteLine("circuitBreaker onReset ");},onHalfOpen: () =>{Console.WriteLine("circuitBreaker onHalfOpen");});var retry = Policy.Handle<HttpRequestException>().RetryAsync(3);var fallback = Policy<string>.Handle<HttpRequestException>().Or<BrokenCircuitException>().FallbackAsync("FALLBACK").WrapAsync(circuitBreaker.WrapAsync(retry));var memberJson = await fallback.ExecuteAsync(async () =>{using (var httpClient = new HttpClient()){httpClient.BaseAddress =new Uri($"http://{memberServiceAddress.Address}:{memberServiceAddress.Port}");var result = await httpClient.GetAsync("/member/" + order.MemberId);result.EnsureSuccessStatusCode();var json = await result.Content.ReadAsStringAsync();return json;}});if (memberJson != "FALLBACK"){var member = JsonConvert.DeserializeObject<MemberVM>(memberJson);vm.Member = member;}

首先定义 circuitBreaker 熔断器策略。这个策略注意最好定义成静态变量。这样能够以整个完整服务的错误为基础来判断是否开启断路器。
然后在业务代码内定义重试策略,降级策略。我们使这些策略一一嵌套。fallback => circuitBreaker => retry ,表示当发生异常的时候首先开始重试, 重试失败后尝试熔断,如果达到熔断的条件就抛出 BrokenCircuitException 异常,降级策略捕获到 HttpRequestException 或者 BrokenCircuitException 进行降级操作。
Polly 还有很多用法比如“缓存”、“隔离” 等策略,这里不在一一演示了。更多请查看文档:https://github.com/App-vNext/Polly/wiki

使用AOP思想改进体验

通过以上对于 Polly 的演示,虽然我们完成了简单的重试、服务降级、熔断等功能。但是显然对于每个方法都去使用 Polly 编写一堆策略的话实在是太麻烦了。那么有什么办法能改进一下 Polly 的使用体验吗?答案是使用 AOP 的思想,通过在执行的方法上打上 Attribute 的方式来指定 Polly 的策略。
下面我们使用 lemon 大佬的 AspectCore AOP 组件结合 Polly 来演示下如何通过 AOP 的思想来处理重试、降级、熔断等策略。

Install-Package AspectCore.Core

通过 nuget 安装 AspectCore 核心类库。

 public class PollyHandleAttribute : AbstractInterceptorAttribute{/// <summary>/// 重试次数/// </summary>public int RetryTimes { get; set; } /// <summary>/// 是否熔断/// </summary>public bool IsCircuitBreaker { get; set; }/// <summary>/// 熔断前的异常次数/// </summary>public int ExceptionsAllowedBeforeBreaking { get; set; }/// <summary>/// 熔断时间/// </summary>public int SecondsOfBreak { get; set; }/// <summary>/// 降级方法/// </summary>public string FallbackMethod { get; set; }/// <summary>/// 一些方法级别统一计数的策略,比如熔断/// </summary>static ConcurrentDictionary<string, AsyncCircuitBreakerPolicy> policyCaches = new ConcurrentDictionary<string, AsyncCircuitBreakerPolicy>();public PollyHandleAttribute(){}public override async Task Invoke(AspectContext context, AspectDelegate next){Context pollyCtx = new Context();pollyCtx["aspectContext"] = context;Polly.Wrap.AsyncPolicyWrap policyWarp = null;var retry = Policy.Handle<HttpRequestException>().RetryAsync(RetryTimes);var fallback = Policy.Handle<Exception>().FallbackAsync(async (fallbackContent, token) =>{AspectContext aspectContext = (AspectContext)fallbackContent["aspectContext"];var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallbackMethod);var fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters);aspectContext.ReturnValue = fallBackResult;}, async (ex, t) => { });AsyncCircuitBreakerPolicy circuitBreaker = null;if (IsCircuitBreaker){var cacheKey = $"{context.ServiceMethod.DeclaringType.ToString()}_{context.ServiceMethod.Name}";if (policyCaches.TryGetValue(cacheKey, out circuitBreaker)){//从缓存内获取该方法的全局熔断策略}else{circuitBreaker = Policy.Handle<Exception>().CircuitBreakerAsync(exceptionsAllowedBeforeBreaking: this.ExceptionsAllowedBeforeBreaking,durationOfBreak: TimeSpan.FromSeconds(this.SecondsOfBreak));policyCaches.TryAdd(cacheKey, circuitBreaker);}}if (circuitBreaker == null){policyWarp = fallback.WrapAsync(retry);}else{policyWarp = fallback.WrapAsync(circuitBreaker.WrapAsync(retry));}await policyWarp.ExecuteAsync(ctx => next(context), pollyCtx);}}

定义一个 PollyHandleAttribute 类,它继承自 AbstractInterceptorAttribute 类,然后实现 Invoke 方法。我们需要在 Invoke 方法内动态构造出 Polly 的相关策略,然后通过 Polly 去执行真正的方法。这里主要需要注意的是熔断策略不能每次新建,因为对于熔断来说是需要全局统计该方法的异常数量来判断是否熔断的,所以需要把熔断策略缓存起来。
这个类参考了 Edison Zhou 大佬的部分代码,原文:Polly+AspectCore实现熔断与降级机制

   public interface IMemberService{Task<MemberVM> GetMemberInfo(string id);MemberVM GetMemberInfoFallback(string id);}
public class MemberService : IMemberService{private IConsulService _consulservice;public MemberService(IConsulService consulService){_consulservice = consulService;}[PollyHandle(IsCircuitBreaker = true, FallbackMethod = "GetMemberInfoFallback", ExceptionsAllowedBeforeBreaking = 5, SecondsOfBreak = 30, RetryTimes = 3)]public async Task<MemberVM> GetMemberInfo(string id){var memberServiceAddresses = await _consulservice.GetServicesAsync("member_center");var memberServiceAddress = memberServiceAddresses.FirstOrDefault();using (var httpClient = new HttpClient()){httpClient.BaseAddress =new Uri($"http://{memberServiceAddress.Address}:{memberServiceAddress.Port}");var result = await httpClient.GetAsync("/member/" + id);result.EnsureSuccessStatusCode();var json = await result.Content.ReadAsStringAsync();if (string.IsNullOrEmpty(json)){return JsonConvert.DeserializeObject<MemberVM>(json);}}return null;}public MemberVM GetMemberInfoFallback(string id){return null;}}

因为我们需要在方法上标记 PollyHandleAttribute ,所以把获取会员相关的逻辑封住进 MemberService 的 GetMemberInfo 方法内。并且在方法上打上Attribute :[PollyHandle(IsCircuitBreaker = true, FallbackMethod = "GetMemberInfoFallback", ExceptionsAllowedBeforeBreaking = 5, SecondsOfBreak = 30, RetryTimes = 3)] 直接通过 AOP 的方式来配置 Polly 的策略,这样就方便了很多。
上面这些配置好之后,下面开始就是如何使 aspectcore 接管 asp.net core 的依赖注入了。根据文档也很简单:

Install-Package AspectCore.Extensions.DependencyInjection

通过 nuget 安装 AspectCore.Extensions.DependencyInjection 包。

  public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{webBuilder.ConfigureKestrel(options =>{options.ListenAnyIP(6001);});webBuilder.UseStartup<Startup>();}).UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());

在 CreateHostBuilder 内使用 UseServiceProviderFactory 替换 ServiceProviderFactory 为 aspectcore 的实现。

     public void ConfigureServices(IServiceCollection services){services.AddSingleton<IMemberService, MemberService>();...services.ConfigureDynamicProxy();}

在 ConfigureServices 方法内配置 IMemberService 的依赖关系以及配置 aspectcore 的动态代理。

总结

通过以上文字我们大致了解了什么是服务降级、什么是熔断。并且通过 Polly 演示了如何处理这些情况。最后使用 lemon 大佬的 AspectCore 封装成一个 Attribute 来演示如何通过 AOP 的思想来简化 Polly 的使用。

谢谢阅读。

演示项目地址

https://github.com/kklldog/myhotel_microservice

相关文章

NET Core with 微服务 - 什么是微服务
.Net Core with 微服务 - 架构图
.Net Core with 微服务 - Ocelot 网关
.Net Core with 微服务 - Consul 注册中心
.Net Core with 微服务 - Seq 日志聚合
.Net Core with 微服务 - Elastic APM
.Net Core with 微服务 - Consul 配置中心

关注我的公众号一起玩转技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300373.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IfElseActivity

IfElseActivity 1.IfElseActivity有两个IfElseBranch子控件&#xff0c;分别作为IfElse的两个分支容器,系统自动添加&#xff0c; 2.其中左边(为真件条)的IfElseBranch容器要设Condition 3.IfElse左边(为真件条)的IfElseBranch容器的Condition有两个条件模式:Code Condition,De…

荷兰人发明的新客机是劈叉的!乘客坐在机翼上

全世界只有3.14 % 的人关注了青少年数学之旅与汽车外型的复杂多变相比&#xff0c;飞机的外型似乎总是那么朴实无华&#xff0c;不管是客机还是战斗机&#xff0c;大约都是大家习以为常的那个样子……但是&#xff0c;终于有人要推陈出新了&#xff01;荷兰皇家航空公司与代尔夫…

预约 .NET Conf: Focus on F# 活动,赢得官方周边!

James: 最近 .NET 基金会预告了将在本月29日底举行的 .NET Conf: Focus on F# 线上活动&#xff0c;预约这次活动还能有机会赢得官方大礼包。.NET Conf: Focus on F# 是一个免费的、为期一天的直播活动&#xff0c;会上有来自社区和使用f#语言的微软团队的演讲者。学习 F# 如何…

.NET Day in China(上海-今日活动)| 线上线下

点击蓝字关注我们活动简介.NET 6 Preview 6 在 7月14日已经发布&#xff0c;.NET 6 是微软开启全平台统一一个 .NET 计划以来的第一个 LTS 版本&#xff0c;意义重大&#xff0c;微软在 .NET 6 引入了 MAUI&#xff0c;跨平台开发将更为简单&#xff0c;ASP.NET Core 也在不断的…

公司重金求数据分析师:为什么90%的公司都需要它?

全世界只有3.14 % 的人关注了青少年数学之旅混迹互联网的同学们&#xff0c;或多或少都对“数据分析师”这一职业有所耳闻。即使你不认识任何数据分析师&#xff0c;也一定看到过这类研究报告或者文章&#xff1a;Smart is the new sexy. 酷炫的图表&#xff0c;理性的分析阐述…

php配置问题汇总

前两天开始跟进PHP&#xff1b;我觉得&#xff0c;PHP的环境配置远比其他语言的要复杂很多。我所说的“其他语言”&#xff0c;包括Java&#xff0c;Oracle&#xff0c;scala&#xff0c;Python等。到现在PHP的环境被搭好&#xff0c;因为是全手动的配置&#xff0c;我完完整整…

Orchard Core 1.0.0 正式发布!

James: Orchard 最早是微软的员工创造的开源项目&#xff0c;使用的技术架构可以说是非常优秀&#xff0c;源码值得学习。功能也非常强大&#xff0c;支持模块化、多租户、工作流等等功能&#xff0c;可以说是 .NET 世界的 WordPress。一开始是.NET Framework 的&#xff0c;在…

[方法“Boolean Contains(System.Guid)”不支持转换为 SQL]的解决办法

Guid ClsID newGuid("d4ee9c52-8d68-4f33-9485-0926281c78ac");IList<Guid>Ids WebProduct.GetAllChildByID(ClsID);var query db.T_Products.Where(p >Ids.Contains((Guid)p.F_ClsID));//这一句编译时无错&#xff0c;但是一执行&#xff0c;就报错出错信息…

解决IE为7939.com的病毒~

病毒名称&#xff1a;“诡秘下载器”变种CXW&#xff08;Trojan.DL.Delf.cxw&#xff09;病毒类型&#xff1a;流氓软件病毒危害级别&#xff1a;★★★☆该病毒运行后会从***指定的网站下载指令并运行&#xff0c;会将用户IE浏览器的主页锁定为一个名叫“7939上网导航”的网站…

这哥们到底是应聘的还是来收购公司的?| 今日趣图

全世界只有3.14 % 的人关注了青少年数学之旅图源网络&#xff0c;侵权删

Abp太重了?轻量化Abp框架

本文首发于个人博客&#xff08;https://blog.zhangchi.fun/&#xff09;在进行框架的选型时&#xff0c;经常会听到“***框架太重了”之类的声音&#xff0c;比如“Abp太重了&#xff0c;不适合我们...”。事实上&#xff0c;Abp框架真的很重吗&#xff1f;框架的“轻”和“重…

六月赞歌

七月的脚步离我们近了&#xff0c;在六月即将过去的时候我是有些话想说的。今年的6月过得很充实&#xff0c;虽谈不上硕果累累&#xff0c;但至于还是收获颇丰。在这最想提的是生活杂谈小组在几位组长们的激情带动&#xff0c;各组员的热情参与下&#xff0c;站到了小组排行榜的…

避不开的分布式事务

前言关于前面系列的文章已经说到分布式服务之间的通信&#xff0c;则分布式事务接下来就是我们要一起学习的主题&#xff0c;走起。数据库事务在现有大大小小的系统中几乎是避免不开的&#xff0c;或多或少总会有一些业务关联在一块&#xff1b;对于单机事务的应用场景和操作&a…

matlab如何求矩阵的转置矩阵,怎么用MATLAB程序求转置矩阵?急需,高手帮忙………………...

在Matlab下输入&#xff1a;edit&#xff0c;然后将下面两行百分号之间的内容&#xff0c;复制进去&#xff0c;保存%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%function ff31(x)f1./[(x-2).^20.1]1./[(x-3).^40.01];%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 返…

任正非一语中的,未来科技的发展核心靠他们!

▲ 点击查看今年6月&#xff0c;华为的任正非接受媒体的采访&#xff0c;多次谈及基础教育&#xff0c;不禁流下了英雄泪&#xff0c;谈话中&#xff0c;曾27次提到了数学&#xff0c;并且表示等自己退休后要找一个好大学&#xff0c;好好地学一学数学。他还谈到了数学对于华…

Windows 2003下×××服务器架设攻略

原文地址[url]http://bbs.51cto.com/thread-49347-1-1.html[/url]Windows 2003下服务器架设攻略笔者有不少朋友因为工作关系常常移动办公&#xff0c;因此资料的传递、与公司信息的及时交流或是累了想在异地打开公司或家里的电脑看看电影等等应用显得很头疼。这方面的应用也就是…

微软出手,蚕食JetBrains系市场?

文 | Travis出品 | OSC开源社区&#xff08;ID&#xff1a;oschina2013&#xff09;近日微软公布了针对 Java 编程语言的 Visual Studio Code 更新路线图&#xff0c;根据路线图的计划&#xff0c;微软将在开发体验、安全、远程开发等方面做出改善。这个更新路线图涵盖了 2021 …

matlab dy,高手,请问用matlab如何解下面方程:y*Dy=a+b*y;我的计算结果里面含有wrightOmega ,怎样解出一般解?...

满意答案weiliyao772013.07.20采纳率&#xff1a;49% 等级&#xff1a;12已帮助&#xff1a;11659人#include #include using namespace std;#define N 20double a[N][N];double x[N1];double b[N1];int n;//n方程个数&#xff0c;n未知数个数int set( ){cout<cin>>…

[005] .NET 的执行模型

.NET大牛之路 • 王亮精致码农 • 2021.07.06前面我们介绍 .NET 历史时讲过&#xff0c;微软基于 .NET Framework 重新设计并创造了跨平台的 .NET Core&#xff0c;目前已经发展到 .NET 5 版本&#xff0c;它的性能较之前的 .NET Framework 有巨大的提升。而 .NET Framework 产…

美国警察开特斯拉追疑犯,时速飙到193公里,然后发现没电了...

全世界只有3.14 % 的人关注了青少年数学之旅晚上11点&#xff0c;时速一度高达193公里&#xff01;美国警察在湾区的一条州际公路上&#xff0c;对嫌犯实施追捕。与往常不同的是&#xff0c;这位警员驾驶的是一辆特斯拉Model S&#xff0c;0到60英里&#xff08;96公里&#xf…