(7)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 利用Polly+AOP+依赖注入封装的降级框架

简介: 创建简单的熔断降级框架 要达到的目标是: 参与降级的方法参数要一样,当HelloAsync执行出错的时候执行HelloFallBackAsync方法。 public class Person {   [HystrixCommand("HelloFallBackAsync")]   pu...

创建简单的熔断降级框架

要达到的目标是: 参与降级的方法参数要一样,当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微服务第二版课件整理出来的

现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!!
C#/.NetCore技术交流群:608188505  欢迎加群交流
如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!
相关文章
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
1081 3
|
人工智能 数据可视化 JavaScript
颠覆开发效率!国内首个微服务编排框架Juggle开源啦!
Juggle是国内首个开源的微服务编排框架,专注于解决企业微服务进程中接口重复开发、系统对接复杂等问题。它提供零代码、低代码和AI增强功能,通过可视化拖拽快速组装简单API为复杂接口,支持多协议、多语言脚本和流程多版本管理。相比国外框架如Conductor,Juggle更贴合国内需求,具备高效开发、企业级可靠性及信创适配等优势,助力企业实现敏捷创新与数字化转型。
1221 0
颠覆开发效率!国内首个微服务编排框架Juggle开源啦!
|
负载均衡 Dubbo 应用服务中间件
框架巨擘:Dubbo如何一统异构微服务江湖,成为开发者的超级武器!
【8月更文挑战第8天】在软件开发中,微服务架构因灵活性和可扩展性备受欢迎。面对异构微服务的挑战,Apache Dubbo作为高性能Java RPC框架脱颖而出。它具备服务注册与发现、负载均衡及容错机制等核心特性,支持多种通信协议和序列化方式,能有效连接不同技术栈的微服务。Dubbo的插件化设计保证了面向未来的扩展性,使其成为构建稳定高效分布式系统的理想选择。
442 80
|
Dubbo Java 应用服务中间件
微服务框架Dubbo环境部署实战
微服务框架Dubbo环境部署的实战指南,涵盖了Dubbo的概述、服务部署、以及Dubbo web管理页面的部署,旨在指导读者如何搭建和使用Dubbo框架。
1368 17
微服务框架Dubbo环境部署实战
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
Kubernetes Java Android开发
用 Quarkus 框架优化 Java 微服务架构的设计与实现
Quarkus 是专为 GraalVM 和 OpenJDK HotSpot 设计的 Kubernetes Native Java 框架,提供快速启动、低内存占用及高效开发体验,显著优化了 Java 在微服务架构中的表现。它采用提前编译和懒加载技术实现毫秒级启动,通过优化类加载机制降低内存消耗,并支持多种技术和框架集成,如 Kubernetes、Docker 及 Eclipse MicroProfile,助力开发者轻松构建强大微服务应用。例如,在电商场景中,可利用 Quarkus 快速搭建商品管理和订单管理等微服务,提升系统响应速度与稳定性。
802 5
|
存储 Java Maven
从零到微服务专家:用Micronaut框架轻松构建未来架构
【9月更文挑战第5天】在现代软件开发中,微服务架构因提升应用的可伸缩性和灵活性而广受欢迎。Micronaut 是一个轻量级的 Java 框架,适合构建微服务。本文介绍如何从零开始使用 Micronaut 搭建微服务架构,包括设置开发环境、创建 Maven 项目并添加 Micronaut 依赖,编写主类启动应用,以及添加控制器处理 HTTP 请求。通过示例代码展示如何实现简单的 “Hello, World!” 功能,并介绍如何通过添加更多依赖来扩展应用功能,如数据访问、验证和安全性等。Micronaut 的强大和灵活性使你能够快速构建复杂的微服务系统。
920 5
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
368 2
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
920 2
|
分布式计算 Java 持续交付
如何选择合适的微服务框架
如何选择合适的微服务框架
486 0