Dynamic Proxy and RPC implementation (Micro Service Focus) netcore in

A call on the RPC

  1. The caller (client Client) so as to initiate a call to a local call;
  2. Client Stub (client stub) after receiving the call charge to be invoked method name and parameters packing encoded into a specific format capable network transmission of the message body;
  3. Client stub the message body sent over the network to the server;
  4. stub Server (server stubs) for unpacking decoder receives a message is received via a network according to the corresponding format, the method name and parameters;
  5 . Server stub according to the method name and parameters of a local call;
  6. callee (Server) performs a local call will return the result to the Stub Server;
  7. the Stub Server returns a value encoded into the message package and send through the network to the client;
  8. Client stub after receiving the message, the unpacking decoder, is returned to the Client;
  9. the final result of this Client RPC calls.

  Reference https://www.cnblogs.com/FG123/p/10261676.html

  Reference https://www.jianshu.com/p/bb9beca7f7bc  fourth quarter

 

Second, the way of thinking about the RPC call (Why use proxy class)

RPC at convenience we have seen,

Assuming the system to call multiple services, if you write a function, every time the name of the service, parameters, and other information to a remote method calls through a service, assume that this method is called getService ( methodName , Object [] , parameter 3 , parameter 4 )    

Let's call each consumer in this method seems to be able to get results.

Where each call remote services should be reflected additional information about the class method name and parameters to be able to pass getService is not too much trouble?

To know the results returned by a remote service for each service will not be the same type that we have every conversion result getService the client, is not too much trouble?

There is no good solution?

  - Use a proxy class, we get various properties of this method in the proxy class reflection agent interface (name & parameters & other), remote call to a remote service, and converting the results obtained. This method looks and above getService Well almost! Then why are we using a proxy class it? ( I do not know, but it looks like it is hanging. ) This does not seem like a very good Moreover if there are multiple classes to invoke a remote service, it would mean a lot to write proxy class?

Thoughts: After calling getService every time the conversion result in the consumer, after the proxy class using this conversion process into a proxy class, so that the consumer would not have to call attention to the type of the result of the conversion of RPC.

 

So people invented a dynamic proxy    - from "Mr. Lu said revolution"

 

It was found that each class should write proxy. Now Xiao Ming to write in the proxy class project in 1000, directly explode on! Explode!.

After the N-generation Xiaoming customer research and found that a method can be summed up in a very provincial effort. - Dynamic Proxy

Simply put: create dynamic proxy class ( https://www.cnblogs.com/netqq/p/11452374.html ), so you do not have to write to each consumer a proxy class, and is not so cool

 

Third, the dynamic proxy and RPC

 The Internet to find a simple example of RPC, very suitable for beginners to learn https://github.com/Coldairarrow/DotNettyRPC 

 After downloading the project to run Server project, and then run the client project

 

Hello see the output of the string on another server console. This is a client program calls IHello.SayHello server (the) service output.

We look at the author's client calls

 

RPCClientFactory source as follows

namespace Coldairarrow.DotNettyRPC 
{ 
    ///  <Summary> 
    /// client factory
     ///  </ Summary> 
    public  class RPCClientFactory 
    { 
        Private  static The ConcurrentDictionary < String , Object > _SERVICES { GET ;} = new new The ConcurrentDictionary < String , Object > ( ); 

        ///  <Summary> 
        /// Get client
         /// Note: The default interface name service name
         ///  </ Summary> 
        ///  <typeParam name = "T"> interface definition type</ typeparam> 
        ///  <param name = "ServerIP"> Remote Service IP </ param> 
        ///  <param name = "Port"> Remote Service Port </ param> 
        ///  <returns A> </ returns A> 
        public  static T getClient <T> ( String ServerIP, int Port) WHERE T: class 
        { 
            return getClient <T> (ServerIP, Port, typeof (T) .Name); 
        } 

        ///  <Summary> 
        /// Get client
         /// Note: The custom service name
         ///  </ the Summary> 
        ///  <typeparam name = "T "> interface definition type</ typeparam> 
        ///  <param name = "ServerIP"> Remote Service IP </ param> 
        ///  <param name = "Port"> Remote Service Port </ param> 
        ///  <param name = "serviceName" > service name </ param> 
        ///  <Returns> </ Returns> 
        public  static T getClient <T> ( String ServerIP, int Port, String serviceName) WHERE T: class 
        { 
            T-service = null ;
             String Key $ = "{serviceName}-{serverIp}-{port}";
            try
            {
                service = (T)_services[key];
            }
            catch
            {
                var clientProxy = new RPCClientProxy
                {
                    ServerIp = serverIp,
                    ServerPort = port,
                    ServiceType = typeof(T),
                    ServiceName = serviceName
                };
                service = clientProxy.ActLike<T>();
                //动态代理?

                _services[key] = service;
            }

            return service;
        }
    }
}
View Code

 

 

 

In the example, the program calls the GetClient 

 

 

 Actually dynamically generated proxy class, returns an object of type IHello.

Let's put aside the author of the program, with our own dynamic proxy class to achieve the same effect.

Add ProxyDecorator <T> class DotNettyRPC project.  Download System.Reflection.DispatchProxy.dll need nuget 

You encounter problems and add ProxyDecorator compile time, we will server, client projects and DotNettyRPC into NETCORE project to work properly executed because  System.Reflection.DispatchProxy.dll only NETCORE class library does not support NET Framework Project

ProxyDecorator <T> source

  1   public class ProxyDecorator<T> : DispatchProxy
  2     {
  3         public string ServerIp { get; set; }
  4         public int ServerPort { get; set; }
  5         public string ServiceName { get; set; }
  6         static Bootstrap _bootstrap { get; }
  7         static ClientWait _clientWait { get; } = new ClientWait();
  8 
  9         static ProxyDecorator()
 10         {
 11             _bootstrap = new Bootstrap()
 12                 .Group(new MultithreadEventLoopGroup())
 13                 .Channel<TcpSocketChannel>()
 14                 .Option(ChannelOption.TcpNodelay, true)
 15                 .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
 16                 {
 17                     IChannelPipeline pipeline = channel.Pipeline;
 18                     pipeline.AddLast("framing-enc", new LengthFieldPrepender(8));
 19                     pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 8, 0, 8));
 20 
 21                     pipeline.AddLast(new ClientHandler(_clientWait));
 22                 }));
 23         }
 24 
 25         public ProxyDecorator()
 26         {
 27 
 28         }
 29 
 30         ///// <summary>
 31         ///// 创建代理实例
 32         ///// </summary>
 33         ///// <param name="decorated">代理的接口类型</param>
 34         ///// <returns></returns>
 35         public T Create(string serverIp, int port, string serviceName)
 36         {
 37 
 38             object proxy = Create<T, ProxyDecorator<T>>();   //调用DispatchProxy 的Create  创建一个新的T
 39             ((ProxyDecorator<T>)proxy).ServerIp = serverIp;
 40             ((ProxyDecorator<T>)proxy).ServerPort = port;
 41             ((ProxyDecorator<T>)proxy).ServiceName = serviceName;
 42             return (T)proxy;
 43         }
 44 
 45         protected override object Invoke(MethodInfo targetMethod, object[] args)
 46         {
 47             if (targetMethod == null) throw new Exception("无效的方法");
 48 
 49             try
 50             {
 51 
 52                 ResponseModel response = null;
 53                 IChannel client = null;
 54                 try
 55                 {
 56                     client = AsyncHelpers.RunSync(() => _bootstrap.ConnectAsync($"{ServerIp}:{ServerPort}".ToIPEndPoint()));
 57                 }
 58                 catch
 59                 {
 60                     throw new Exception("连接到服务端失败!");
 61                 }
 62                 if (client != null)
 63                 {
 64                     _clientWait.Start(client.Id.AsShortText());
 65                     RequestModel requestModel = new RequestModel
 66                     {
 67                         ServiceName = ServiceName,
 68                         MethodName = targetMethod.Name,
 69                         Paramters = args.ToList()
 70                     };
 71                     var sendBuffer = Unpooled.WrappedBuffer(requestModel.ToJson().ToBytes(Encoding.UTF8));
 72 
 73                     client.WriteAndFlushAsync(sendBuffer);
 74                     var responseStr = _clientWait.Wait(client.Id.AsShortText()).ResponseString;
 75                     response = responseStr.ToObject<ResponseModel>();
 76                 }
 77                 else
 78                 {
 79                     throw new Exception("连接到服务端失败!");
 80                 }
 81 
 82                 if (response == null)
 83                     throw new Exception("服务器超时未响应");
 84                 else if (response.Success)
 85                 {
 86                     Type returnType = targetMethod.ReturnType;
 87                     if (returnType == typeof(void))
 88                         return null;
 89                     else
 90                          return response.Data;
 91                 }
 92                 else
 93                     throw new Exception($"服务器异常,错误消息:{response.Msg}");
 94 
 95             }
 96             catch (Exception ex)
 97             {
 98                 if (ex is TargetInvocationException)
 99                 {
100                     LogException(ex.InnerException ?? ex, targetMethod);
101                     throw ex.InnerException ?? ex;
102                 }
103                 else
104                 {
105                     throw ex;
106                 }
107             }
108         }
109 
110 
111         /// <summary>
112         /// aop异常的处理
113         /// </summary>
114         /// <param name="exception"></param>
115         /// <param name="methodInfo"></param>
116         private void LogException(Exception exception, MethodInfo methodInfo = null)
117         {
118             try
119             {
120                 var errorMessage = new StringBuilder();
121                 errorMessage.AppendLine($"Class {methodInfo.IsAbstract.GetType().FullName}");
122                 errorMessage.AppendLine($"Method {methodInfo?.Name} threw exception");
123                 errorMessage.AppendLine(exception.Message);
124 
125                 //_logError?.Invoke(errorMessage.ToString());  记录到文件系统
126             }
127             catch (Exception)
128             {
129                 // ignored  
130                 //Method should return original exception  
131             }
132         }
View Code

 

这个类的源码与上一篇反向代理文章中所讲的核心区别是 Invoke 的实现,上篇文章中其调用的是本地的一个类实体的方法,本文中其调用的是远程服务中的类实体的方法

client调用代码如下

 static void Main(string[] args)
        {
            //IHello client = RPCClientFactory.GetClient<IHello>("127.0.0.1", 39999);
            var serviceProxy = new ProxyDecorator<IHello>();
            IHello client = serviceProxy.Create("127.0.0.1", 39999, "IHello");
            client.SayHello("Hello");
            Console.WriteLine("完成");
            Console.ReadLine();
        }

 重新启动Server 和Client 执行效果如下

 

和原作者的执行结果一致,那么我们换个接口来试试:创建IMail  和Mail两个类,并包含一个成员string  Send(string  name)//IMail和Mail的成员  

public string Send(string name)
        {
       Console.WriteLine(name);
return $"你的名字是{name}"; }

 

 

Client端调用代码

        static void Main(string[] args)
        {
            //IHello client = RPCClientFactory.GetClient<IHello>("127.0.0.1", 39999);
            //var serviceProxy = new ProxyDecorator<IHello>();
            //IHello client = serviceProxy.Create("127.0.0.1", 39999, "IHello");
            var serviceProxy = new ProxyDecorator<IMail>();
            IMail client = serviceProxy.Create("127.0.0.1", 39999, "IMail");
            string   msg= client.Send("张三丰");
            Console.WriteLine(msg);
            Console.WriteLine("完成");
            Console.ReadLine();
        }

 

服务端添加服务监控

        static void Main(string[] args)
        {
            RPCServer rPCServer = new RPCServer(39999);
            rPCServer.RegisterService<IHello, Hello>();
            rPCServer.RegisterService<IMail, Mail>();
            rPCServer.Start();

            Console.ReadLine();
        }

 

 

预计客户端输出:

你的名字是张三丰

完成 

服务端输出是:

张三丰

我们先后启动server 和 client 两个端来看看

 

 

至此动态代理的应用示例已经演示完毕。

在查看   寒空飞箭   git 源码时候我们发现  RPCClientProxy 类和我们的ProxyDecorator<T> 类  实现了相同的效果,寒空飞箭的实现方式也是很令人振奋,独辟蹊径,非常值得学习。下篇文章将会分析他的用法。感兴趣的可以自行查看作者的源码。

参考文献

https://www.cnblogs.com/coldairarrow/p/10193765.html

 说明:

RPC功能的实现是直接引用作者 寒空飞箭 的代码,对此向 寒空飞箭 表示感谢

 

Guess you like

Origin www.cnblogs.com/netqq/p/11462054.html