動的プロキシとRPCの実装(マイクロサービスフォーカス)netcoreで

RPCの呼び出し

  1.発信者(クライアントクライアント)ローカルコールへのコールを開始するように、
  前記クライアント・スタブ(クライアント・スタブ)通話料金を受信した後には、メソッド名とパラメータは、特定のフォーマット可能なネットワークにエンコードパッキン呼び出されますメッセージ本体の送信;
  3.クライアント・スタブサーバにネットワークを介して送信されるメッセージの本体、
  4スタブサーバ(サーバ・スタブ)デコーダをアンパックするための対応する形式、メソッド名とパラメータに従って、ネットワークを介してメッセージが受信された受信し、
  5 。サーバ・スタブのメソッド名とローカルコールのパラメータに応じて、
  ; 6.呼び出し先(サーバ)、ローカルコールがスタブサーバーに結果を返す実行
  7スタブサーバーは、メッセージパッケージに符号化された値を返し、クライアントにネットワークを介して送信します。
  8.クライアント・スタブメッセージ、アンパックデコーダを受信した後、クライアントに返される。
  9このクライアントRPCコールの最終結果。

  参考https://www.cnblogs.com/FG123/p/10261676.html

  参考https://www.jianshu.com/p/bb9beca7f7bc  第四四半期

 

第二に、RPC呼び出しの考え方(なぜ使用プロキシクラス)

私たちが見てきた都合の良いときにRPC、

この方法は、のgetService(と呼ばれていることを前提とし、あなたが関数を書く場合は、リモートメソッドへの毎回のサービス、パラメータ、およびその他の情報の名前は、サービスを通じて呼び出し、複数のサービスを呼び出すためのシステムを想定するとmethodNameのオブジェクト[] パラメータ3パラメータ4)    

のは、この方法では、各消費者が結果を得ることができるように思わ呼ぶことにしましょう。

どこ各呼び出しは、リモートサービスはのgetServiceがあまりにも面倒ではありません渡すことができるようにクラスメソッド名とパラメータに関する追加情報を反映すべきですか?

私たちは、クライアントのgetServiceすべての変換結果を持って、同じタイプになりません各サービスのリモートサービスから返された結果を知るためには、あまりにも面倒ではないですか?

良い解決策はありませんか?

  -プロキシクラスを使用して、我々は、プロキシクラス反射エージェントインタフェース(名・パラメータやその他)、リモートサービスへのリモート呼び出し、そして結果が得られた変換は、この方法の種々の特性を得ます。この方法では、見て、ほとんどのgetServiceまあ以上!そして、なぜ我々は、プロキシクラスを使用していますか?私は知らないが、それがぶら下がっているように見えます。)これは非常に良いように見えるしていません。また、リモートサービスを呼び出すための複数のクラスが存在する場合、それはプロキシクラスを記述するために多くのことを意味するでしょうか?

思考:消費者はRPCの変換結果の種類に注意を呼び出す必要がないように、プロキシクラスにこの変換プロセスを使用したプロキシクラスの後、消費者に毎回変換結果をのgetServiceを呼び出した後。

 

だから、人々は、動的プロキシを発明   - 「呂氏は革命と」から

 

これは、各クラスがプロキシを書く必要があることが判明しました。今暁明は、直接に爆発し、1000年にプロキシクラスのプロジェクトに書くために!爆発します!

N-世代暁明の顧客調査の後と方法は非常に地方の努力にまとめることができることを見出しました。 - 動的プロキシ

簡単に言えば:動的プロキシクラスを作成します(https://www.cnblogs.com/netqq/p/11452374.html)、あなたは各コンシューマプロキシクラスを記述する必要はありませんので、そしてとてもクールではありません

 

第三に、動的プロキシとRPC

 RPCの簡単な例を見つけるためにインターネット、初心者が学ぶことのために非常に適しhttps://github.com/Coldairarrow/DotNettyRPC 

 Serverプロジェクトを実行し、クライアントのプロジェクトを実行するプロジェクトをダウンロードした後、

 

こんにちは、別のサーバコンソールに文字列の出力を参照してください。これは、クライアントプログラムはIHello.SayHelloサーバ()サービス出力を呼び出すです。

私たちは、著者のクライアント呼び出しを見て

 

RPCClientFactoryソース次のように

名前空間Coldairarrow.DotNettyRPC 
{ 
    ///  <要約> 
    /// クライアント工場
     ///  </要約> 
    パブリック クラスRPCClientFactory 
    { 
        プライベート 静的 ConcurrentDictionary < 文字列はオブジェクト > _SERVICESは{ GET ;} = 新しい新ザ・ConcurrentDictionary < 文字列オブジェクト > ( ); 

        ///  <要約> 
        /// 取得し、クライアント
         /// 注:デフォルトのインタフェース名、サービス名
         ///  </要約> 
        ///  <typeParam名= "T"> インターフェース定義タイプ</ typeparam> 
        ///  <PARAM NAME = "SERVERIP"> リモートサービスIP </ param>の
        ///  <PARAM NAME = "ポート"> リモートサービスポート</ param>の
        ///  <返しA> </ Aが返されます> 
        公共の 静的 T getClient <T>(文字列 SERVERIP、int型ポート)T:クラス
        { 返す getClient <T>(SERVERIP、港、typeof演算(T).NAME); 
        } /// <要約> /// クライアントを取得
         /// 注:カスタムサービス名
         /// </要約> /// <typeparam名= "T「> インターフェース定義タイプ
            

         
         
         </ typeparam> 
        ///  <PARAM NAME = "SERVERIP"> リモートサービスIP </ param>の
        ///  <PARAM NAME = "ポート"> リモートサービスポート</ param>の
        ///  <PARAM NAME = "serviceNameを" > サービス名</ param>の
        ///  <戻り値> </戻り値> 
        パブリック 静的 T getClient <T>(文字列 SERVERIP、int型ポート、文字列 serviceNameの)WHERE T:クラス
        { 
            T-サービス = ヌル;
             文字列のキー$ = "{serviceNameを} - {SERVERIP} - {ポート} 試す
            { 
                サービス = (T)_services [キー]。
            } 
            キャッチ
            { 
                VAR clientProxy = 新しいRPCClientProxy 
                { 
                    SERVERIP = SERVERIP、
                    のServerPort = ポート、
                    サービス種別 = typeof演算(T)
                    のServiceName = serviceNameを
                }。
                サービス = clientProxy.ActLike <T> ();
                // 动态代理?

                _services [キー] = サービス。
            } 

            戻りサービス。
        } 
    } 
}
コードの表示

 

 

 

例では、プログラムはGetClientを呼び出します 

 

 

 実際に、動的に生成されたプロキシクラス、タイプIHelloのオブジェクトを返します。

のは、同じ効果を達成するために、当社独自の動的プロキシクラスで、プログラムの作者を脇に置くましょう。

ProxyDecorator <T>クラスDotNettyRPCプロジェクトを追加します。  ダウンロードSystem.Reflection.DispatchProxy.dllはnugetを必要とします 

あなたは問題が発生し、ProxyDecoratorを追加し、時間をコンパイル、我々は、サーバー、クライアントのプロジェクトとDotNettyRPCはので、適切に実行に動作するNETCOREプロジェクトになります  System.Reflection.DispatchProxy.dllのみNETCOREクラスライブラリは、NET Frameworkのプロジェクトをサポートしていません。

ProxyDecorator <T>ソース

  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功能的实现是直接引用作者 寒空飞箭 的代码,对此向 寒空飞箭 表示感谢

 

おすすめ

転載: www.cnblogs.com/netqq/p/11462054.html