(7)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 利用Polly+AOP+依赖注入封装的降级框架
创建简单的熔断降级框架
要达到的目标是: 参与降级的方法参数要一样,当HelloAsync执行出错的时候执行HelloFallBackAsync方法。
public class Person
{
[HystrixCommand("HelloFallBackAsync")] public virtual async Task<string> HelloAsync(string name)
{
Console.WriteLine("hello"+name); return "ok";
}
public async Task<string> HelloFallBackAsync(string name)
{
Console.WriteLine("执行失败"+name); return "fail";
}}
1、编写 HystrixCommandAttribute
using AspectCore.DynamicProxy;
using System;
using System.Threading.Tasks;
namespace hystrixtest1
{
//限制这个特性只能标注到方法上
[AttributeUsage(AttributeTargets.Method)]
public class HystrixCommandAttribute : AbstractInterceptorAttribute
{
public HystrixCommandAttribute(string fallBackMethod)
{
this.FallBackMethod = fallBackMethod;
}
public string FallBackMethod { get; set; }
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
try
{
await next(context);//执行被拦截的方法
}
catch (Exception ex)
{
//context.ServiceMethod被拦截的方法。context.ServiceMethod.DeclaringType被拦截方法所在的类
//context.Implementation实际执行的对象p
//context.Parameters方法参数值
//如果执行失败,则执行FallBackMethod
//调用降级方法
//1.调用降级的方法(根据对象获取类,从类获取方法)
var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod);
//2.调用降级的方法
Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters);
//3.把降级方法的返回值返回
context.ReturnValue = fallBackResult;
}
}
}
}
2、编写类
public class Person//需要public类
{
[HystrixCommand(nameof(HelloFallBackAsync))] public virtual async Task<string> HelloAsync(string name)//需要是虚方法
{
Console.WriteLine("hello"+name);
String s = null;
// s.ToString(); return "ok";
}
public async Task<string> HelloFallBackAsync(string name)
{
Console.WriteLine("执行失败"+name); return "fail";
}
[HystrixCommand(nameof(AddFall))] public virtual int Add(int i,int j)
{
String s = null; //s.ToArray(); return i + j;
}
public int AddFall(int i, int j)
{
return 0;
}
}
3、创建代理对象
ProxyGeneratorBuilder proxyGeneratorBuilder = new ProxyGeneratorBuilder(); using (IProxyGenerator proxyGenerator = proxyGeneratorBuilder.Build())
{
Person p = proxyGenerator.CreateClassProxy<Person>();
Console.WriteLine(p.HelloAsync("yzk").Result);
Console.WriteLine(p.Add(1, 2));
}
上面的代码还支持多次降级,方法上标注[HystrixCommand]并且virtual即可:
public class Person//需要public类
{
[HystrixCommand(nameof(Hello1FallBackAsync))] public virtual async Task<string> HelloAsync(string name)//需要是虚方法
{
Console.WriteLine("hello" + name); String s = null; s.ToString(); return "ok";
}
[HystrixCommand(nameof(Hello2FallBackAsync))] public virtual async Task<string> Hello1FallBackAsync(string name)
{
Console.WriteLine("Hello降级1" + name); String s = null; s.ToString(); return "fail_1";
}
public virtual async Task<string> Hello2FallBackAsync(string name)
{
Console.WriteLine("Hello降级2" + name);
return "fail_2";
}
[HystrixCommand(nameof(AddFall))] public virtual int Add(int i, int j)
{
String s = null; s.ToString(); return i + j;
}
public int AddFall(int i, int j)
{
return 0;
}
}
细化框架
上面明白了了原理,然后直接展示写好的更复杂的HystrixCommandAttribute,讲解代码。
这是杨中科老师维护的开源项目
github最新地址 https://github.com/yangzhongke/RuPeng.HystrixCore
Nuget地址:https://www.nuget.org/packages/RuPeng.HystrixCore
重试:MaxRetryTimes表示最多重试几次,如果为0则不重试,RetryIntervalMilliseconds 表示重试间隔的毫秒数;
熔断:EnableCircuitBreaker是否启用熔断,ExceptionsAllowedBeforeBreaking表示熔断前出现允许错误几次,MillisecondsOfBreak表示熔断多长时间(毫秒);
超时:TimeOutMilliseconds执行超过多少毫秒则认为超时(0表示不检测超时)
缓存:CacheTTLMilliseconds 缓存多少毫秒(0 表示不缓存),用“类名+方法名+所有参数值 ToString拼接”做缓存Key(唯一的要求就是参数的类型ToString对于不同对象一定要不一样)。
用到了缓存组件:Install-Package Microsoft.Extensions.Caching.Memory
using System;
using AspectCore.DynamicProxy;
using System.Threading.Tasks;
using Polly;
namespace RuPeng.HystrixCore
{
[AttributeUsage(AttributeTargets.Method)]
public class HystrixCommandAttribute : AbstractInterceptorAttribute
{
/// <summary>
/// 最多重试几次,如果为0则不重试
/// </summary>
public int MaxRetryTimes { get; set; } = 0;
/// <summary>
/// 重试间隔的毫秒数
/// </summary>
public int RetryIntervalMilliseconds { get; set; } = 100;
/// <summary>
/// 是否启用熔断
/// </summary>
public bool EnableCircuitBreaker { get; set; } = false;
/// <summary>
/// 熔断前出现允许错误几次
/// </summary>
public int ExceptionsAllowedBeforeBreaking { get; set; } = 3;
/// <summary>
/// 熔断多长时间(毫秒)
/// </summary>
public int MillisecondsOfBreak { get; set; } = 1000;
/// <summary>
/// 执行超过多少毫秒则认为超时(0表示不检测超时)
/// </summary>
public int TimeOutMilliseconds { get; set; } = 0;
/// <summary>
/// 缓存多少毫秒(0表示不缓存),用“类名+方法名+所有参数ToString拼接”做缓存Key
/// </summary>
public int CacheTTLMilliseconds { get; set; } = 0;
//由于CircuitBreaker要求同一段代码必须共享同一个Policy对象。
//而方法上标注的Attribute 对于这个方法来讲就是唯一的对象,一个方法对应一个方法上标注的Attribute对象。
//一般我们熔断控制是针对一个方法,一个方法无论是通过几个 Person 对象调用,无论是谁调用,只要全局出现ExceptionsAllowedBeforeBreaking次错误,就会熔断,这是框架的实现,你如果认为不合理,自己改去。
//我们在Attribute上声明一个Policy的成员变量,这样一个方法就对应一个Policy对象。
private Policy policy;
private static readonly Microsoft.Extensions.Caching.Memory.IMemoryCache memoryCache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions());
/// <summary>
///
/// </summary>
/// <param name="fallBackMethod">降级的方法名</param>
public HystrixCommandAttribute(string fallBackMethod)
{
this.FallBackMethod = fallBackMethod;
}
public string FallBackMethod { get; set; }
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
//一个HystrixCommand中保持一个policy对象即可
//其实主要是CircuitBreaker要求对于同一段代码要共享一个policy对象
//根据反射原理,同一个方法就对应一个HystrixCommandAttribute,无论几次调用,
//而不同方法对应不同的HystrixCommandAttribute对象,天然的一个policy对象共享
//因为同一个方法共享一个policy,因此这个CircuitBreaker是针对所有请求的。
//Attribute也不会在运行时再去改变属性的值,共享同一个policy对象也没问题
lock (this)//因为Invoke可能是并发调用,因此要确保policy赋值的线程安全
{
if (policy == null)
{
policy = Policy.NoOpAsync();//创建一个空的Policy
if (EnableCircuitBreaker) //先保证熔断
{
policy = policy.WrapAsync(Policy.Handle<Exception>().CircuitBreakerAsync(ExceptionsAllowedBeforeBreaking, TimeSpan.FromMilliseconds(MillisecondsOfBreak)));
}
if (TimeOutMilliseconds > 0) //控制是否超时
{
policy = policy.WrapAsync(Policy.TimeoutAsync(() => TimeSpan.FromMilliseconds(TimeOutMilliseconds), Polly.Timeout.TimeoutStrategy.Pessimistic));
}
if (MaxRetryTimes > 0) //如果出错等待MaxRetryTimes时间在执行
{
policy = policy.WrapAsync(Policy.Handle<Exception>().WaitAndRetryAsync(MaxRetryTimes, i => TimeSpan.FromMilliseconds(RetryIntervalMilliseconds)));
}
Policy policyFallBack = Policy
.Handle<Exception>() //出错了报错 如果出错就尝试调用降级方法
.FallbackAsync(async (ctx, t) =>
{
//这里拿到的就是ExecuteAsync(ctx => next(context), pollyCtx);这里传的 pollyCtx
AspectContext aspectContext = (AspectContext)ctx["aspectContext"];
var fallBackMethod = context.ServiceMethod.DeclaringType.GetMethod(this.FallBackMethod);
Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters);
//不能如下这样,因为这是闭包相关,如果这样写第二次调用Invoke的时候context指向的
//还是第一次的对象,所以要通过Polly的上下文来传递AspectContext
//context.ReturnValue = fallBackResult;
aspectContext.ReturnValue = fallBackResult;
}, async (ex, t) => { });
policy = policyFallBack.WrapAsync(policy);
}
}
//把本地调用的AspectContext传递给Polly,主要给FallbackAsync中使用,避免闭包的坑
Context pollyCtx = new Context();//Context是polly中通过Execute给FallBack、Execute等回调方法传上下文对象使用的
pollyCtx["aspectContext"] = context;//context是aspectCore的上下文
//Install-Package Microsoft.Extensions.Caching.Memory
if (CacheTTLMilliseconds > 0)
{
//用类名+方法名+参数的下划线连接起来作为缓存key
string cacheKey = "HystrixMethodCacheManager_Key_" + context.ServiceMethod.DeclaringType + "." + context.ServiceMethod + string.Join("_", context.Parameters);
//尝试去缓存中获取。如果找到了,则直接用缓存中的值做返回值
if (memoryCache.TryGetValue(cacheKey, out var cacheValue))
{
context.ReturnValue = cacheValue;
}
else
{
//如果缓存中没有,则执行实际被拦截的方法
await policy.ExecuteAsync(ctx => next(context), pollyCtx);
//存入缓存中
using (var cacheEntry = memoryCache.CreateEntry(cacheKey))
{
cacheEntry.Value = context.ReturnValue;//返回值放入缓存
cacheEntry.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMilliseconds(CacheTTLMilliseconds);
}
}
}
else//如果没有启用缓存,就直接执行业务方法
{
await policy.ExecuteAsync(ctx => next(context), pollyCtx);
}
}
}
}
框架不是万能的,不用过度框架,过度框架带来的复杂度陡增,从人人喜欢变成人人恐惧。
结合 asp.net core依赖注入
在asp.net core项目中,可以借助于asp.net core的依赖注入,简化代理类对象的注入,不用再自己调用ProxyGeneratorBuilder 进行代理类对象的注入了。
Install-Package AspectCore.Extensions.DependencyInjection
修改Startup.cs的ConfigureServices方法,把返回值从void改为IServiceProvider
using AspectCore.Extensions.DependencyInjection;
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<Person>();
return services.BuildAspectCoreServiceProvider();
}
其 中 services.AddSingleton<Person>(); 表 示 把Person注 入 。
BuildAspectCoreServiceProvider是让aspectcore接管注入。
在Controller中就可以通过构造函数进行依赖注入了:
public class ValuesController : Controller
{
private Person p;
public ValuesController(Person p)
{
this.p = p;
}
}
通过反射扫描所有Service类,只要类中有标记了CustomInterceptorAttribute的方法都算作服务实现类。为了避免一下子扫描所有类,所以 RegisterServices 还是手动指定从哪个程序集中加载。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
RegisterServices(this.GetType().Assembly, services); return services.BuildAspectCoreServiceProvider();
}
private static void RegisterServices(Assembly asm, IServiceCollection services)
{
//遍历程序集中的所有public类型
foreach (Type type in asm.GetExportedTypes())
{
//判断类中是否有标注了CustomInterceptorAttribute的方法
bool hasCustomInterceptorAttr = type.GetMethods().Any(m => m.GetCustomAttribute(typeof(CustomInterceptorAttribute)) != null);
if (hasCustomInterceptorAttr)
{
services.AddSingleton(type);
}
}
}
注:此文章是我看杨中科老师的.Net Core微服务第二版和.Net Core微服务第二版课件整理出来的
(7)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 利用Polly+AOP+依赖注入封装的降级框架的更多相关文章
- 【新书推荐】《ASP.NET Core微服务实战:在云环境中开发、测试和部署跨平台服务》 带你走近微服务开发
<ASP.NET Core 微服务实战>译者序:https://blog.jijiechen.com/post/aspnetcore-microservices-preface-by-tr ...
- ASP.NET Core 微服务初探[1]:服务发现之Consul
ASP.NET Core 微服务初探[1]:服务发现之Consul 在传统单体架构中,由于应用动态性不强,不会频繁的更新和发布,也不会进行自动伸缩,我们通常将所有的服务地址都直接写在项目的配置文件 ...
- (8)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Ocelot网关(Api GateWay)
说到现在现有微服务的几点不足: 1) 对于在微服务体系中.和 Consul 通讯的微服务来讲,使用服务名即可访问.但是对于手 机.web 端等外部访问者仍然需要和 N 多服务器交互,需要记忆他们的服务 ...
- (5)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 熔断降级(Polly)
一. 什么是熔断降级 熔断就是“保险丝”.当出现某些状况时,切断服务,从而防止应用程序不断地尝试执行可能会失败的操作给系统造成“雪崩”,或者大量的超时等待导致系统卡死. 降级的目的是当某个服务提供者发 ...
- (1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型
开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题, ...
- (10)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Ocelot+Identity Server
用 JWT 机制实现验证的原理如下图: 认证服务器负责颁发 Token(相当于 JWT 值)和校验 Token 的合法性. 一. 相关概念 API 资源(API Resource):微博服务器接口. ...
- (6)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- AOP框架
AOP 框架基础 要求懂的知识:AOP.Filter.反射(Attribute). 如果直接使用 Polly,那么就会造成业务代码中混杂大量的业务无关代码.我们使用 AOP (如果不了解 AOP,请自 ...
- ASP.NET Core微服务+Tabler前端框架搭建个人博客1--开始前想说的话
写在前面 本人为在读研究生,特别喜欢.NET,觉得.NET的编程方式.语法都特别友好,学习.NET Core已经差不多有一年半了,从一开始不知道如何入门到现在终于可以编写一些小的应用程序,想一想还是非 ...
- (11)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Thrift高效通讯 (完结)
一. 什么是 RPC Restful 采用 Http 进行通讯,优点是开放.标准.简单.兼容性升级容易: 缺点是性能略低.在 QPS 高或者对响应时间要求苛刻的服务上,可以用 RPC(Remote P ...
随机推荐
- JAVA 设计模式遵循的六大基本准则
JAVA 设计模式遵循的六大基本准则 一.单一职责原则:(Single Responsibility Pinciple) 一个类只负责一项职责. 当超过一项职责需要负责时,需要增加新的类来负责新的职 ...
- 常用的Git命令整理
之前一直忙于项目苦于没有时间总结,今天刚好有时间特来总结一下在工作中常用到的代码版本管理器Git.至于为什么要用Git?Git相比SVN有哪些好处?我就不多说了,前人已经总结的很好.今天主要介绍的是常 ...
- 阿里云 centos7 django + uWSGI+Nginx + python3 部署攻略
centos7+nginx+python3+django+uwsgi配置Django 项目部署 1.租的服务器(选择centos)的话,需要在阿里云后台控制台开放几个端口,克隆一下已开放的端口,t ...
- 07-OpenLDAP密码审计
OpenLDAP密码审计 阅读视图 密码审计的作用 操作实践 1. 密码审计的作用 开启密码审计的功能主要用于记录OpenLDAP用户修改密码,以及密码审计. 2. 操作实践 开启密码审计模块并配置密 ...
- fedora 使用 vnc 远程 fedora 28 主机
问题重述: 最近环境中需要使用 fedora 28 来进行远程桌面,于是就重新配置了一下vnc 在这里面做一下记录. 过程: yum 安装: 工作机上安装 vncviewer 远程主机上安装: vnc ...
- 成功激活Win8.1专业版方法
前两天安装了Win8.1,感觉界面效果都很简约,速度很快,很喜欢,但是不是破解版,本想将就着用,等到真正破解工具出来了再激活,但是用了两天发现系统右下角屏幕上有未激活的提示,时常弹出激活设置,看着有点 ...
- nginx limit_rate突然限速失败
##问题 nginx限制用户对指定目录的访问: <!-- lang: shell --> location ~ ^/(path001)/ { limit_rate 0k; limit_co ...
- GsonUtil工具类
import java.util.ArrayList; import java.util.List; import java.util.Map; import com.google.gson.Gson ...
- [转]mysql和redis的区别
转自https://www.cnblogs.com/zxh1297/p/9394108.html 1.mysql和redis的数据库类型 mysql是关系型数据库,主要用于存放持久化数据,将数据存储在 ...
- Java设计模式之四 ----- 适配器模式和桥接模式
前言 在上一篇中我们学习了创建型模式的建造者模式和原型模式.本篇则来学习下结构型模式的适配器模式和桥接模式. 适配器模式 简介 适配器模式是作为两个不兼容的接口之间的桥梁.这种类型的设计模式属于结构型 ...