.net core 3.1 基于AspectCore+Polly 实现熔断降级
这次学习主要参考了Edison zhou 的这篇文章:https://www.cnblogs.com/edisonchou/p/9159644.html,相关的概念也做了解释,有兴趣的可以了解一下
本次实现需要引用以下包,版本也有要求(包括Autofac),稍后会进行解释。 我先把实现逻辑讲完,再分享其中遇到的坑
如果对AspectCore不太了解的,可以下载查看AspectCoreDemo ,了解AspectCore拦截器设置。
使用AspectCore前,把原来的Castle相关引用删除,避免冲突,同时前文提到的 LogInterceptor , MyInterceptor 先注释相关代码,后续会使用 AspectCore 实现基于方法的拦截
首先新建自定义拦截类CustomInterceptorAttribute ,这里要using AspectCore.DynamicProxy;
/// <summary> /// 自定义拦截器 /// </summary> public class CustomInterceptorAttribute : AbstractInterceptorAttribute { /// <summary> /// 每个被拦截的方法中执行 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public override async Task Invoke(AspectContext context, AspectDelegate next) { try { Console.WriteLine("Before service call"); await next(context); // 执行被拦截的方法 } catch (Exception) { Console.WriteLine("Service threw an exception"); throw; } finally { Console.WriteLine("After service call"); } } }
依赖注入是借助Autofac ,在Program.cs 增加如下代码 .UseServiceProviderFactory(new AutofacServiceProviderFactory())
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //autofac 依赖注入 .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
在Startup.cs 的ConfigureContainer 方法最后加入一行代码 添加引用 AspectCore.Extensions.Autofac
builder.RegisterDynamicProxy();
AspectCore 配置完成了,接下来是 Polly+AspectCore 结合使用
这里引用杨中科老师封装的 RuPeng.HystrixCore ,里面定义好了相关Polly配置和执行逻辑,后续只要添加相关属性值就可以实现动态配置。
同步引用 AspectCore.Core, Microsoft.Extensions.Caching.Memory(对熔断后操作数据做缓存)
接下来是调用方法。
新建IProductService.cs
在相关的接口添加Polly属性配置,具体的属性含义可以查看 RuPeng.HystrixCore 源代码,GetAllProductsFallBackAsync 是Fallback接口
public interface IProductService { [HystrixCommand(nameof(GetAllProductsFallBackAsync), EnableCircuitBreaker = true, CacheTTLMilliseconds =1000*6, ExceptionsAllowedBeforeBreaking = 2, MillisecondsOfBreak = 1000 * 6)] string GetAllProductsAsync(string productType); string GetAllProductsFallBackAsync(string productType); }
ProductService.cs
public class ProductService : IProductService { public string GetAllProductsAsync(string productType) { NLogHelper.logger.Info("test for GetAllProductsAsync:" + productType); string str = null; str.ToString(); // to do : using HttpClient to call outer service to get product list return $"OK {productType}"; } public string GetAllProductsFallBackAsync(string productType) { NLogHelper.logger.Info("test for GetAllProductsFallBackAsync:"+productType); return $"OK for FallBack {productType}"; } }
新建ProductController.cs
[Route("api/[controller]/[action]")] [ApiController] public class ProductController : ControllerBase { private readonly IProductService _baseService; public ProductController(IProductService baseService) { _baseService = baseService; } [HttpGet] public async Task<string> Get(string productType="A") { var product = _baseService.GetAllProductsAsync(productType); return product; } }
注意,Get方法用异步线程
最后调试:
调试完,接着说说版本号的问题以及遇到的坑
最初引用 AspectCore.Extensions.Autofac 注入匿名代理时,无法编译通过,提示Method not found: 'Autofac.Builder.DeferredCallback Autofac.ContainerBuilder.RegisterCallback(System.Action`1<Autofac.Core.IComponentRegistry>)'.
后来 在AspectCore-Framwork 项目提交issues ,解释说已处理但没发布,暂时可以先降级Autofac到4.9 版本,于是把相关的引用降级,编译通过
在引用 RuPeng.HystrixCore 调试时有个bug,fallback接口没有把参数传过来,导致每次传不同参数调用接口都是返回之前的参数值,后来下载源码修改
Object fallBackResult = fallBackMethod.Invoke(context.Implementation, context.Parameters);
改为
Object fallBackResult = fallBackMethod.Invoke(context.Implementation, aspectContext.Parameters);
编译后替换,问题解决
此外RuPeng.HystrixCore 不兼容Polly 7 ,所以要把Polly 版本降为6.0.1