动态代理配合rpc技术调用远程服务,不用关注细节的实现,让程序就像在本地调用以用。

因此动态代理在微服务系统中是不可或缺的一个技术。网上看到大部分案例都是通过反射自己实现,且相当复杂。编写和调试相当不易,我这里提供里一种简便的方式来实现动态代理。

1、创建我们的空白.netcore项目

通过vs2017轻易的创建出一个.netcore项目

2、编写Startup.cs文件

默认Startup 没有构造函数,自行添加构造函数,带有  IConfiguration 参数的,用于获取项目配置,但我们的示例中未使用配置

  1. public Startup(IConfiguration configuration)
  2. {
  3. Configuration = configuration;
  4. //注册编码提供程序
  5. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  6. }

在ConfigureServices 配置日志模块,目前core更新的很快,日志的配置方式和原来又很大出入,最新的配置方式如下

  1. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
  2. public void ConfigureServices(IServiceCollection services )
  3. {
  4. services.AddLogging(loggingBuilder=> {
  5. loggingBuilder.AddConfiguration(Configuration.GetSection("Logging"));
  6. loggingBuilder.AddConsole();
  7. loggingBuilder.AddDebug();
  8. });
  9.  
  10. }

添加路由过滤器,监控一个地址,用于调用我们的测试代码。netcore默认没有UTF8的编码方式,所以要先解决UTF8编码问题,否则将在输出中文时候乱码。

这里注意 Map  内部传递了参数 applicationBuilder ,千万不要 使用Configure(IApplicationBuilder app, IHostingEnvironment env ) 中的app参数,否则每次请求api/health时候都将调用这个中间件(app.Run会短路期后边所有的中间件),

  1. app.Map("/api/health", (applicationBuilder) =>
  2. {
  3.  
  4. applicationBuilder.Run(context =>
  5. {
  6. return context.Response.WriteAsync(uName.Result, Encoding.UTF8);
  7. });
  8. });

3、代理

netcore 已经为我们完成了一些工作,提供了DispatchProxy 这个类

  1. #region 程序集 System.Reflection.DispatchProxy, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  2. // C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.2.0\ref\netcoreapp2.2\System.Reflection.DispatchProxy.dll
  3. #endregion
  4.  
  5. namespace System.Reflection
  6. {
  7. //
  8. public abstract class DispatchProxy
  9. {
  10. //
  11. protected DispatchProxy();
  12.  
  13. //
  14. // 类型参数:
  15. // T:
  16. //
  17. // TProxy:
  18. public static T Create<T, TProxy>() where TProxy : DispatchProxy;
  19. //
  20. // 参数:
  21. // targetMethod:
  22. //
  23. // args:
  24. protected abstract object Invoke(MethodInfo targetMethod, object[] args);
  25. }
  26. }

这个类提供了一个实例方法,一个静态方法:

Invoke(MethodInfo targetMethod, object[] args) (注解1)

Create<T, TProxy>()(注解2)

Create 创建代理的实例对象,实例对象在调用方法时候会自动执行Invoke

首先我们创建一个动态代理类  ProxyDecorator<T>:DispatchProxy  需要继承DispatchProxy 

在DispatchProxy 的静态方法  Create<T, TProxy>()中要求 TProxy : DispatchProxy

ProxyDecorator 重写  DispatchProxy的虚方法invoke

我们可以在ProxyDecorator 类中添加一些其他方法,比如:异常处理,MethodInfo执行前后的处理。

重点要讲一下 

ProxyDecorator 中需要传递一个 T类型的变量 decorated 。

因为  DispatchProxy.Create<T, ProxyDecorator<T>>(); 会创建一个新的T的实例对象 ,这个对象是代理对象实例,我们将 decorated 绑定到这个代理实例上

接下来这个代理实例在执行T类型的任何方法时候都会用到 decorated,因为 [decorated] 会被传给  targetMethod.Invoke(decorated, args)   (targetMethod 来自注解1) ,invoke相当于执行

这样说的不是很明白,我们直接看代码

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8.  
  9. namespace TestWFW
  10. {
  11. public class ProxyDecorator<T> : DispatchProxy
  12. {
  13. //关键词 RealProxy
  14. private T decorated;
  15. private event Action<MethodInfo, object[]> _afterAction; //动作之后执行
  16. private event Action<MethodInfo, object[]> _beforeAction; //动作之前执行
  17.  
  18. //其他自定义属性,事件和方法
  19. public ProxyDecorator()
  20. {
  21. }
  22.  
  23. /// <summary>
  24. /// 创建代理实例
  25. /// </summary>
  26. /// <param name="decorated">代理的接口类型</param>
  27. /// <returns></returns>
  28. public T Create(T decorated)
  29. {
  30. object proxy = Create<T, ProxyDecorator<T>>(); //调用DispatchProxy 的Create 创建一个新的T
  31. ((ProxyDecorator<T>)proxy).decorated = decorated; //这里必须这样赋值,会自动未proxy 添加一个新的属性
  1.                                             //其他的请如法炮制
  1.           return (T)proxy;
  2. }

  3. /// <summary>
  4. /// 创建代理实例
  5. /// </summary>
  6. /// <param name="decorated">代理的接口类型</param>
  7. /// <param name="beforeAction">方法执行前执行的事件</param>
  8. /// <param name="afterAction">方法执行后执行的事件</param>
  9. /// <returns></returns>
  10. public T Create(T decorated, Action<MethodInfo, object[]> beforeAction, Action<MethodInfo, object[]> afterAction)
  11. {
  12.  
  13. object proxy = Create<T, ProxyDecorator<T>>(); //调用DispatchProxy 的Create 创建一个新的T
  14. ((ProxyDecorator<T>)proxy).decorated = decorated;
  15. ((ProxyDecorator<T>)proxy)._afterAction = afterAction;
  16. ((ProxyDecorator<T>)proxy)._beforeAction = beforeAction;
  17. //((GenericDecorator<T>)proxy)._loggingScheduler = TaskScheduler.FromCurrentSynchronizationContext();
  18. return (T)proxy;
  19. }
  20.  
  21. protected override object Invoke(MethodInfo targetMethod, object[] args)
  22. {
  23. if (targetMethod == null) throw new Exception("无效的方法");
  24. try
  25. {
  26. //_beforeAction 事件
  27. if (_beforeAction != null)
  28. {
  29. this._beforeAction(targetMethod, args);
  30. }
  31. object result = targetMethod.Invoke(decorated, args);
  32.              System.Diagnostics.Debug.WriteLine(result);      //打印输出面板
  33. var resultTask = result as Task;
  34. if (resultTask != null)
  35. {
  36. resultTask.ContinueWith(task => //ContinueWith 创建一个延续,该延续接收调用方提供的状态信息并执行 当目标系统 tasks。
  37. {
  38. if (task.Exception != null)
  39. {
  40. LogException(task.Exception.InnerException ?? task.Exception, targetMethod);
  41. }
  42. else
  43. {
  44. object taskResult = null;
  45. if (task.GetType().GetTypeInfo().IsGenericType &&
  46. task.GetType().GetGenericTypeDefinition() == typeof(Task<>))
  47. {
  48. var property = task.GetType().GetTypeInfo().GetProperties().FirstOrDefault(p => p.Name == "Result");
  49. if (property != null)
  50. {
  51. taskResult = property.GetValue(task);
  52. }
  53. }
  54. if (_afterAction != null)
  55. {
  56. this._afterAction(targetMethod, args);
  57. }
  58. }
  59. });
  60. }
  61. else
  62. {
  63. try
  64. {
  65. // _afterAction 事件
  66. if (_afterAction != null)
  67. {
  68. this._afterAction(targetMethod, args);
  69. }
  70. }
  71. catch (Exception ex)
  72. {
  73. //Do not stop method execution if exception
  74. LogException(ex);
  75. }
  76. }
  77. return result;
  78. }
  79. catch (Exception ex)
  80. {
  81. if (ex is TargetInvocationException)
  82. {
  83. LogException(ex.InnerException ?? ex, targetMethod);
  84. throw ex.InnerException ?? ex;
  85. }
  86. else
  87. {
  88. throw ex;
  89. }
  90. }
  91. }
  92.  
  93. /// <summary>
  94. /// aop异常的处理
  95. /// </summary>
  96. /// <param name="exception"></param>
  97. /// <param name="methodInfo"></param>
  98. private void LogException(Exception exception, MethodInfo methodInfo = null)
  99. {
  100. try
  101. {
  102. var errorMessage = new StringBuilder();
  103. errorMessage.AppendLine($"Class {decorated.GetType().FullName}");
  104. errorMessage.AppendLine($"Method {methodInfo?.Name} threw exception");
  105. errorMessage.AppendLine(exception.Message);
  106.  
  107. //_logError?.Invoke(errorMessage.ToString()); 记录到文件系统
  108. }
  109. catch (Exception)
  110. {
  111. // ignored
  112. //Method should return original exception
  113. }
  114. }
  115. }
  116. }

代码比较简单,相信大家都看的懂,关键的代码和核心的系统代码已经加粗和加加粗+红 标注出来了

DispatchProxy<T> 类中有两种创建代理实例的方法

我们看一下第一种比较简单的创建方法

  1. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  2. public void Configure(IApplicationBuilder app, IHostingEnvironment env )
  3. {
  4. if (env.IsDevelopment())
  5. {
  6. app.UseDeveloperExceptionPage();
  7. }
  8.  
  9. // 添加健康检查路由地址
  10. app.Map("/api/health", (applicationBuilder) =>
  11. {
  12.  
  13. applicationBuilder.Run(context =>
  14. {
  15. IUserService userService = new UserService();
  16. //执行代理
  17. var serviceProxy = new ProxyDecorator<IUserService>();
  18. IUserService user = serviceProxy.Create(userService); //
  19. Task<string> uName = user.GetUserName();
  20. context.Response.ContentType = "text/plain;charset=utf-8";
  21.  
  22. return context.Response.WriteAsync(uName.Result, Encoding.UTF8);
  23. });
  24. });
  25. }

代码中 UserService 和 IUserService 是一个class和interface  自己实现即可 ,随便写一个接口,并用类实现,任何一个方法就行,下面只是演示调用方法时候会执行什么,这个方法本身在岩石中并不重要。

由于ProxyDecorator 中并未注入相关事件,所以我们在调用 user.GetUserName(222) 时候看不到任何特别的输出。下面我们演示一个相对复杂的调用方式。

  1. // 添加健康检查路由地址
  2. app.Map("/api/health2", (applicationBuilder) =>
  3. {
  4. applicationBuilder.Run(context =>
  5. {
  6. IUserService userService = new UserService();
  7. //执行代理
  8. var serviceProxy = new ProxyDecorator<IUserService>();
  9. IUserService user = serviceProxy.Create(userService, beforeEvent, afterEvent); //
  10. Task<string> uName = user.GetUserName();
  11. context.Response.ContentType = "text/plain;charset=utf-8";
  12.  
  13. return context.Response.WriteAsync(uName.Result, Encoding.UTF8);
  14.  
  15. });
  16. });
  1. IUserService user = serviceProxy.Create(userService, beforeEvent, afterEvent); 在创建代理实例时候传递了beforEvent afterEvent,这两个事件处理
    函数是我们在业务中定义的

void beforeEvent(MethodInfo methodInfo, object[] arges)
{
System.Diagnostics.Debug.WriteLine("方法执行前");
}

void afterEvent(MethodInfo methodInfo, object[] arges)
{
System.Diagnostics.Debug.WriteLine("执行后的事件");
}

在代理实例执行接口的任何方法的时候都会执行  beforeEvent,和 afterEvent 这两个事件(请参考Invoke(MethodInfo targetMethod, object[] args) 方法的实现)

在我们的示例中将会在vs的 输出 面板中看到

方法执行前

“方法输出的值”

执行后事件

我们看下运行效果图:

 这是 user.GetUserName(222) 的运行结果

控制台输出结果

netcore 之动态代理(微服务专题)的更多相关文章

  1. 【微服务专题之】.Net6下集成消息队列上-RabbitMQ

    ​ 微信公众号:趣编程ACE关注可了解更多的.NET日常实战开发技巧,如需源码 请公众号后台留言 源码;[如果觉得本公众号对您有帮助,欢迎关注] .Net中RabbitMQ的使用 [微服务专题之].N ...

  2. netcore 中的动态代理与RPC实现(微服务专题)

    一.关于RPC的调用 1. 调用者(客户端Client)以本地调用的方式发起调用: 2. Client stub(客户端存根)收到调用后,负责将被调用的方法名.参数等打包编码成特定格式的能进行网络传输 ...

  3. .netcore 3.1高性能微服务架构:webapi规范

    1.1 定义 1.基础接口:单一职责原则,每个接口只负责各自的业务,下接db,通用性强. 2.聚合接口:根据调用方需求聚合基础接口数据,业务性强. 1.2 协议 1. 客户端在通过 API 与后端服务 ...

  4. java架构之路-(微服务专题)初步认识微服务与nacos初步搭建

    历史演变: 以前我们都是一个war包,包含了很多很多的代码,反正我开始工作的时候做的就是这样的项目,一个金融系统,代码具体多少行记不清楚了,内部功能超多,但是实际能用到的不多,代码冗余超大,每次部署大 ...

  5. java架构之路-(微服务专题)nacos集群精讲实战

    上次回顾: 上次博客,我们主要说了微服务的发展历程和nacos集群单机的搭建,单机需要-m standalone启动,集群建议使用nginx做一下反向代理,自行保证mysql和ngxin的高可用. 本 ...

  6. java架构之路-(微服务专题)ribbon的基本使用和内部算法的自我实现

    上次回归: 上次我们主要说了,我们的注册中心nacos的使用,如我们的命名空间.分组.集群.版本等是如何使用的,如果是这样呢?我们现在有三个用户服务和三个订单服务,我们应该如何分发这些请求呢?都请求到 ...

  7. .netcore 3.1高性能微服务架构:封装调用外部服务的接口方法--HttpClient客户端思路分析

    众所周知,微服务架构是由一众微服务组成,项目中调用其他微服务接口更是常见的操作.为了便于调用外部接口,我们的常用思路一般都是封装一个外部接口的客户端,使用时候直接调用相应的方法.webservice或 ...

  8. java架构之路-(微服务专题)feign的基本使用和nacos的配置中心

    上次回归: 上次我们说了ribbon的基本使用,包括里面的内部算法,算法的细粒度配置,还有我们自己如何实现我们自己的算法,主要还是一些基本使用的知识,还不会使用ribbon的小伙伴可以回去看一下上一篇 ...

  9. .netcore 3.1高性能微服务架构:加入swagger接口文档

    本文为原创文章:首发:http://www.zyiz.net/tech/detail-108663.html swagger是什么? Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视 ...

随机推荐

  1. js,ts操作dom总结

    以上面为例: js获取placeholder节点 : document.getElementsByClassName("newTicket")[0].getAttributeNod ...

  2. .NET Core CSharp初级篇 1-8泛型、逆变与协变

    .NET Core CSharp初级篇 1-8 本节内容为泛型 为什么需要泛型 泛型是一个非常有趣的东西,他的出现对于减少代码复用率有了很大的帮助.比如说遇到两个模块的功能非常相似,只是一个是处理in ...

  3. C语言编程入门之--第三章编写第一个C语言程序

    第三章 编写第一个C语言程序 导读:一般学一门计算机语言的第一堂上机课(“上机”顾名思义,上了计算机),就是往屏幕输出“hello world”,本章也不例外. 1.1 Hello,World! 这一 ...

  4. 洛谷P1003 题解

    题面 思路一:纯模拟.(暴力不是满分) 思路: 1.定义一个二维数组. 2.根据每个数据给二维数组赋值. 3.最后输出那个坐标的值. 思路二(正规思路): 逆序找,因为后来的地毯会覆盖之前的,一发现有 ...

  5. 【JDK】JDK源码分析-TreeMap(2)

    前文「JDK源码分析-TreeMap(1)」分析了 TreeMap 的一些方法,本文分析其中的增删方法.这也是红黑树插入和删除节点的操作,由于相对复杂,因此单独进行分析. 插入操作 该操作其实就是红黑 ...

  6. 【Java】Exception thrown by the agent : java.rmi.server.ExportException: Port already in use: 1099

    详细信息如下: Error: Exception thrown by the agent : java.rmi.server.ExportException: Port already in use: ...

  7. 泥瓦匠 5 年 Java 的成长感悟(下)

    继续<泥瓦匠 5 年 Java 的成长感悟(上)>,大致包括下面几点: 学技术的心态 学技术的学法 工作的心态 工作的硬技能 工作的软实力 听点雷子的民谣,我就安静地感概感概.上次说写的, ...

  8. poj 1068 模拟

    题目链接 大概题意就是告诉你有个n个小括号,每一个")"左边有多少个"("都告诉你了,然后让你求出每一对括号之间有多少对括号(包含自己本身). 思路: 我先计算 ...

  9. 1. 源码分析---SOFARPC可扩展的机制SPI

    这几天离职在家,正好没事可以疯狂的输出一下,本来想写DUBBO的源码解析的,但是发现写DUBBO源码的太多了,所以找一个写的不那么多的框架,所以就选中SOFARPC这个框架了. SOFARPC是蚂蚁金 ...

  10. 使用 OpenSSL 为 Nginx 创建自签名证书 并开启客户端身份验证

    本文章默认读者了解Openssl,CA,网站证书相关知识,直接实战!配置完成后,浏览器会显示"安全的HTTPS"连接.不会像其他文章那样,是红色警告的证书提示. 准备环境 笔者使用 ...