<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      一、關(guān)于RPC的調(diào)用

        1. 調(diào)用者(客戶端Client)以本地調(diào)用的方式發(fā)起調(diào)用;
        2. Client stub(客戶端存根)收到調(diào)用后,負責(zé)將被調(diào)用的方法名、參數(shù)等打包編碼成特定格式的能進行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;
        3. Client stub將消息體通過網(wǎng)絡(luò)發(fā)送給服務(wù)端;
        4. Server stub(服務(wù)端存根)收到通過網(wǎng)絡(luò)接收到消息后按照相應(yīng)格式進行拆包解碼,獲取方法名和參數(shù);
        5. Server stub根據(jù)方法名和參數(shù)進行本地調(diào)用;
        6. 被調(diào)用者(Server)本地調(diào)用執(zhí)行后將結(jié)果返回給server stub;
        7. Server stub將返回值打包編碼成消息,并通過網(wǎng)絡(luò)發(fā)送給客戶端;
        8. Client stub收到消息后,進行拆包解碼,返回給Client;
        9. Client得到本次RPC調(diào)用的最終結(jié)果。

        參考https://www.cnblogs.com/FG123/p/10261676.html
      <https://www.cnblogs.com/FG123/p/10261676.html>

        參考https://www.jianshu.com/p/bb9beca7f7bc
      <https://www.jianshu.com/p/bb9beca7f7bc>?第四節(jié)

      ?

      二、關(guān)于RPC調(diào)用方式的思考(為什么要用代理類)

      RPC的方便之處我們已經(jīng)看到了,

      假設(shè)在系統(tǒng)中要調(diào)用多個服務(wù),如果寫一個函數(shù),每次將這個服務(wù)的名字,參數(shù),和其他信息通過一個方法來調(diào)用遠程服務(wù),假設(shè)這個方法叫做getService(
      methodname,object[],參數(shù)3,參數(shù)4)? ??

      我們在各個消費類中來調(diào)用這個方法似乎也能得到結(jié)果。

      在每個調(diào)用遠程服務(wù)的地方都要反射出 類的方法名稱,參數(shù)等其他信息以能傳給getService 是不是很麻煩?

      要知道遠程服務(wù)每個服務(wù)返回的結(jié)果不會是一樣的類型,那我們在客戶端還要每次都轉(zhuǎn)換getService的結(jié)果,是不是很麻煩?

      有沒有好的解決方案?


        --請使用代理類,我們在代理類中反射代理接口得到這個方法的各種屬性(名稱&參數(shù)&其他),遠程調(diào)用傳遞給遠程服務(wù),并轉(zhuǎn)換得到的結(jié)果。看起來這種方法和上文的getService
      差不多嘛!那我們?yōu)槭裁匆褂么眍惸??(我也不知道,但看起來很吊的樣子。)這看起來并沒有很好的樣子,況且如果有多個類要調(diào)用遠程服務(wù),那豈不是要寫很多代理類?

      思考:調(diào)用getService 后每次都要在消費類中轉(zhuǎn)換結(jié)果,使用代理類后將這個轉(zhuǎn)換過程放入了代理類中,這樣消費類就不用關(guān)注RPC的調(diào)用結(jié)果的類型的轉(zhuǎn)換了。

      ?

      于是人們發(fā)明了動態(tài)代理? ?--來自《魯迅先生說革命》

      ?

      人們發(fā)現(xiàn)每個類都要寫個代理?,F(xiàn)在小明要在項目中寫1000個代理類,直接氣炸了,對!炸了!。

      經(jīng)過了N代的小明客戶鉆研和發(fā)現(xiàn),總結(jié)了一套可以很省力氣的方法。--動態(tài)代理

      簡單的來說:動態(tài)創(chuàng)建代理類(https://www.cnblogs.com/netqq/p/11452374.html
      <https://www.cnblogs.com/netqq/p/11452374.html>),這樣就不用給每個消費類都寫一個代理類了,是不很爽

      ?

      三、動態(tài)代理與RPC

      ?在網(wǎng)上找到了一個簡單的RPC 示例,非常適合初學(xué)者學(xué)習(xí)??https://github.com/Coldairarrow/DotNettyRPC?

      ?下載項目后先運行 Server 項目,再運行client項目



      ?

      看到再server的控制臺上輸出了hello 字符串。這是客戶端程序調(diào)用了server的IHello.SayHello()的服務(wù)輸出的。

      我們來看下作者的客戶端調(diào)用

      ?

      RPCClientFactory源碼如下
      namespace Coldairarrow.DotNettyRPC { /// <summary> /// 客戶端工廠 /// </summary>
      public class RPCClientFactory { private static ConcurrentDictionary<string,
      object> _services { get; } = new ConcurrentDictionary<string, object>(); ///
      <summary> /// 獲取客戶端 /// 注:默認服務(wù)名為接口名 /// </summary> /// <typeparam name="T">
      接口定義類型</typeparam> /// <param name="serverIp">遠程服務(wù)IP</param> /// <param
      name="port">遠程服務(wù)端口</param> /// <returns></returns> public static T GetClient<T>(
      string serverIp, int port) where T : class { return GetClient<T>(serverIp, port,
      typeof(T).Name); } /// <summary> /// 獲取客戶端 /// 注:自定義服務(wù)名 /// </summary> ///
      <typeparam name="T">接口定義類型</typeparam> /// <param name="serverIp">遠程服務(wù)IP</param>
      /// <param name="port">遠程服務(wù)端口</param> /// <param name="serviceName">服務(wù)名</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>(); //動態(tài)代理? _services[key] = service; } return service;
      } } } View Code
      ?

      ?

      ?

      在示例中,程序調(diào)用的GetClient?



      ?

      ?

      ?實際上也是動態(tài)生成的代理類,返回了IHello類型的對象。

      我們先拋開該作者的程序,用我們自己的動態(tài)代理類來實現(xiàn)相同的效果。

      在DotNettyRPC項目中添加ProxyDecorator<T> 類。? ?
      需要nuget下載System.Reflection.DispatchProxy.dll

      在添加ProxyDecorator 和編譯的時候會遇到問題,我們將server、 client項目和DotNettyRPC?
      轉(zhuǎn)為NETCORE項目才能正常執(zhí)行 ,因為?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 /////
      創(chuàng)建代理實例 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>>(); //
      調(diào)用DispatchProxy 的Create 創(chuàng)建一個新的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("連接到服務(wù)端失敗!"); 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("連接到服務(wù)端失敗!"); 80 } 81 82 if (response == null) 83 throw new
      Exception("服務(wù)器超時未響應(yīng)"); 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($"服務(wù)器異常,錯誤消息:{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()); 記錄到文件系統(tǒng) 126 } 127 catch (Exception)
      128 { 129 // ignored 130 //Method should return original exception 131 } 132 }
      View Code
      ?

      這個類的源碼與上一篇反向代理文章中所講的核心區(qū)別是 Invoke 的實現(xiàn),上篇文章中其調(diào)用的是本地的一個類實體的方法,本文中其調(diào)用的是遠程服務(wù)中的類實體的方法


      client調(diào)用代碼如下
      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 執(zhí)行效果如下



      ?

      和原作者的執(zhí)行結(jié)果一致,那么我們換個接口來試試:創(chuàng)建IMail? 和Mail兩個類,并包含一個成員string? Send(string? name)//
      IMail和Mail的成員??
      public string Send(string name) {
      ?      Console.WriteLine(name); return $"你的名字是{name}"; }

      ?

      ?

      Client端調(diào)用代碼
      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(); }
      ?

      服務(wù)端添加服務(wù)監(jiān)控
      static void Main(string[] args) { RPCServer rPCServer = new RPCServer(39999);
      rPCServer.RegisterService<IHello, Hello>(); rPCServer.RegisterService<IMail,
      Mail>(); rPCServer.Start(); Console.ReadLine(); }
      ?

      ?

      預(yù)計客戶端輸出:

      你的名字是張三豐

      完成?

      服務(wù)端輸出是:

      張三豐

      我們先后啟動server 和 client 兩個端來看看



      ?

      ?

      至此動態(tài)代理的應(yīng)用示例已經(jīng)演示完畢。

      在查看? ?寒空飛箭 <https://www.cnblogs.com/coldairarrow/>? ?git
      源碼時候我們發(fā)現(xiàn)??RPCClientProxy 類和我們的ProxyDecorator<T> 類?
      實現(xiàn)了相同的效果,寒空飛箭的實現(xiàn)方式也是很令人振奮,獨辟蹊徑,非常值得學(xué)習(xí)。下篇文章將會分析他的用法。感興趣的可以自行查看作者的源碼。

      參考文獻:

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

      ?說明:

      RPC功能的實現(xiàn)是直接引用作者?寒空飛箭?的代碼,對此向?寒空飛箭?表示感謝

      ?

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點信息
      問題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          4kfree性满足欧美hd18 | 成人性生活电影两区 三区 四区 五区 | 亚洲日韩欧美国产 | 国产一级a毛一级a看免费视频黑人 | 中国大乳女人的hdmove | 伊人日日 | 五月婷婷中文 | 中文字幕日韩精品人妻 | 成人性爱电影网 | 91浏览器在线视频 |