日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NET Core微服务之路:让我们对上一个Demo通讯进行修改,完成RPC通讯

發布時間:2023/12/18 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NET Core微服务之路:让我们对上一个Demo通讯进行修改,完成RPC通讯 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近一段時間有些事情耽擱了更新,抱歉各位了。 上一篇我們簡單的介紹了DotNetty通信框架,并簡單的介紹了基于DotNetty實現了回路(Echo)通信過程。 我們來回憶一下上一個項目的整個流程:
  • 當服務端啟動后,綁定并監聽(READ)設定的端口,比如1889。
  • 當客戶端啟動后,綁定指定端口,等待用戶輸入。
  • 當用戶輸入任意字符串數據后,客戶端將這組數據進行轉碼為byte格式進行傳輸到服務端。
  • 當服務端收到客戶端傳來的數據,進行轉碼后輸出控制臺,并將這組數據再次回傳到客戶端。
  • 客戶端收到數據,也打印出來。
  • ?

    很簡單的實現了一個點對點的通信例子。接下來我們將對這個DEMO進行簡單的修改,模擬最簡單的gRPC通信的一個構造過程。 本篇很簡單,只要實現了上一個demo,稍作修改,就能實現gRPC了(當然實際構建gRPC根本不會這么簡單),本篇也是順帶一下這幾天搞出來的一個輕量級RPC框架,先接上一個例子。

    服務端

    增加兩個靜態方法SayHello和SayByebye,用于提供遠程調用,超級簡單,不解釋。

    public static class Say {public static string SayHello(string content){return $"hello {content}";}public static string SayByebye(string content){return $"byebye {content}";} }

    ?

    ?

    在我們原來的ChannelRead函數中,將原有的Echo回路傳輸,直接替換成如下內容。

    1 public override void ChannelRead(IChannelHandlerContext context, object message) 2 { 3 if (message is IByteBuffer buffer) 4 { 5 Console.WriteLine($"message length is {buffer.Capacity}"); 6 var obj = JsonConvert.DeserializeObject<Dictionary<string, string>>(buffer.ToString(Encoding.UTF8).Replace(")", "")); // (1) 7 8 byte[] msg = null; 9 if (obj["func"].Contains("sayHello")) // (2) 10 { 11 msg = Encoding.UTF8.GetBytes(Say.SayHello(json["username"])); 12 } 13 14 if (obj["func"].Contains("sayByebye")) // (2) 15 { 16 msg = Encoding.UTF8.GetBytes(Say.SayByebye(json["username"])); 17 } 18 19 if (msg == null) return; 20 // 設置Buffer大小 21 var b = Unpooled.Buffer(msg.Length, msg.Length); // (3) 22 IByteBuffer byteBuffer = b.WriteBytes(msg); // (4) 23 context.WriteAsync(byteBuffer); // (5) 24 } 25 }

    ?

    ?

    (1):有這樣一句話Replace(")", ""),筆者不知為何每次傳送過來從buffer里轉義出來的字符串,始終會有一個左括號在里面,也許是消息頭,也許是protobuf-net的標記頭,因為都是byte格式,在服務端偷懶就沒有再進行一次protobuf的反序列化了。 為何要用Dictionary來作為中間對象轉換,因為序列化需要實體對象作為類型,為了簡單的介紹RPC,目前也就這么干了,例如上面代碼所示。 (2):通過判斷“func”字段中的內容進行方法調用,并將調用過程的返回結果轉為BYTE格式。 (3):設置本次傳輸中的Buffer大小。 (4):將消息(數據)寫入到DotNetty的Buffer。 (5):最終將Buffer寫入到當前上下文(包含通道,傳輸對象,連接對象等等)。

    客戶端

    我們將上一個demo中的EchoClientHandler做如下修改,以完成一個簡單的請求

    1 public EchoClientHandler() 2 { 3 var hello = new Dictionary<string, string> // (1) 4 { 5 {"func", "sayHello"}, 6 {"username", "stevelee"} 7 }; 8 SendMessage(ToStream(JsonConvert.SerializeObject(hello))); 9 } 10 11 private byte[] ToStream(string msg) 12 { 13 Console.WriteLine($"string length is {msg.Length}"); 14 using (var stream = new MemoryStream()) // (2) 15 { 16 Serializer.Serialize(stream, msg); 17 return stream.ToArray(); 18 } 19 } 20 21 private void SendMessage(byte[] msg) 22 { 23 Console.WriteLine($"byte length is {msg.Length}"); 24 _initialMessage = Unpooled.Buffer(msg.Length, msg.Length); 25 _initialMessage.WriteBytes(msg); // (3) 26 }

    ?

    (1):建立與服務端相關的通信數據。 (2):將數據序列化為二進制流。 (3):將數據寫入到ByteBuffer中。

    啟動一下

    由于在客戶端明文標注了使用sayHello這個方法,客戶端會收到服務端返回的"hello stevelee"。

      這樣一個最簡單的RPC遠程調用就完成了(其實上一篇就也屬于RPC,只是這里用方法和過濾來指定調用)。

    ?

    ?問題

  • 服務端不可能都通過這樣笨拙的過濾方式來調用方法吧?是的,這只是DEMO,為了演示和理解基礎概念而已,而是要動過動態代理來實現方法Invoke。
  • 這個DEMO只是一個點對點的遠程調用,不會涉及到任何服務路由和轉發等高級特性。
  • 有新的接口的時候時候,需要重新編譯和暴露,如果有上萬個新的接口,這樣的重復工作豈不是瘋了。
  • ...etc
  • 這里推薦一下最近構建的一個小框架:Easy.Rpc(連接點我),實現了路由,轉發,代理,動態編譯的特性。這里也幫朋友們推薦一個同樣基于DotNetty的RPC框架(連接點我)張隊推薦我加入他們,可我不知道怎么加入他們的團隊,悲催啊... 簡單介紹一下使用方法,本篇不詳細介紹這個框架是如何實現的,估計會好幾十萬字,單獨擰出來做個系列會更好,框架設計需要哪些原則,需要考慮到的問題,包含設計模式、依賴注入、動態代理、動態編譯、路由轉發等等特性。

    Esay.Rpc

    正如上面提到問題,需要解決這些問題,就需要修改諸多內容, 例如把函數改為接口,把接口的定義放置服務端并對外開放相應端口,把接口的實現同樣放置服務端,提供接口的調用,客戶端通過類似API的方式進行遠程接口調用,因此這個接口的定義必須單列的一個項目; 如何將接口自動部署(暴露)出來,可以通過中間協調器(也叫服務注冊中心,如ETCD,consul,zookeeper),如何將這些接口自動注冊到服務中心呢,需要實現反射自動掃描并添加到注冊中心。 我們添加一個Rpc.Common的中間通用庫,當然Easy.Rpc的框架源碼也在這個里面(框架目前不探討),添加IUserService接口,UserModel實體類,UserServiceImpl實現類。其實通用類庫只需要接口和實體就行,接口實現完全放置服務端,這樣這個庫也能完全分離出來。(不過筆者偷懶都寫到Rpc.Common庫中去了,實際生產決不能這么膜,分離,分離,分離,這也是微服務的主要概念之一) DEMO結構如下(Easy.Rpc源碼目前也包含在這個里面,過兩天單獨拎出來做成框架,方便調用)

    ?

    ?

    先看看接口定義了些什么:

    1 /// <summary> 2 /// 接口UserService的定義 3 /// </summary> 4 [RpcTagBundle] 5 public interface IUserService 6 { 7 Task<string> GetUserName(int id); 8 9 Task<int> GetUserId(string userName); 10 11 Task<DateTime> GetUserLastSignInTime(int id); 12 13 Task<UserModel> GetUser(int id); 14 15 Task<bool> Update(int id, UserModel model); 16 17 Task<IDictionary<string, string>> GetDictionary(); 18 19 Task Try(); 20 21 Task TryThrowException(); 22 } 8個接口,幾乎囊括了目前RPC調用測試的所有方法場景。接口實現就不貼了,你完全可以自定義接口的任何實現,或者就一句Console.Write("哇涼哇涼完啦")都可以。 接口參數中有個UserModel的實體對象,這里也貼上來。 1 [ProtoContract] 2 public class UserModel 3 { 4 [ProtoMember(1)] public string Name { get; set; } 5 6 [ProtoMember(2)] public int Age { get; set; } 7 }

    ?

    上面有兩個不一樣的標記,也是protobuf-net中獨有的特性。

    ProtoContract標記:該類是參與序列化內容的數據類。 ProtoMember標題:該類需要序列化的字段和順序。

    ?

    protobuf-net的坑

  • 默認例子中該類沒有任何繼承,因此不會存在一個妖孽問題,但如果UserModel是一個子類,他繼承于一個父類,而這個父類也同樣擁有多個子類,直接ProtoContract參與序列化將會報錯,需要在特性上增加DataMemberOffset = x,此處的x不是字母,而是這個子類的一個序列化順序。比如有3個子類繼承同一個父類,前面兩個子類的偏移量分別是1和2,那么這個類的偏移量將設置為3,以此類推。
  • 默認的數據類型中,系統定義的標準類型沒問題,但有個妖孽的int[]這樣的數組類型,那也將是個噩夢,官網團隊沒有解釋為何不支持數組的序列化,我猜測估計是因為數組的不規則性(比如多維數組、甚至不規則的多維數組)而放棄了這個類型的序列化,畢竟序列化是不能影響性能的。
  • 接下來繼續服務端的代碼

    1 static void Main() 2 { 3 var bTime = DateTime.Now; 4 5 // 實現自動裝配 6 var serviceCollection = new ServiceCollection(); 7 { 8 serviceCollection 9 .AddLogging() 10 .AddRpcCore() 11 .AddService() 12 .UseSharedFileRouteManager("d:\\routes.txt") 13 .UseDotNettyTransport(); 14 15 // ** 注入本地測試類 16 serviceCollection.AddSingleton<IUserService, UserServiceImpl>(); 17 } 18 19 // 構建當前容器 20 var buildServiceProvider = serviceCollection.BuildServiceProvider(); 21 22 // 獲取服務管理實體類 23 var serviceEntryManager = buildServiceProvider.GetRequiredService<IServiceEntryManager>(); 24 var addressDescriptors = serviceEntryManager.GetEntries().Select(i => new ServiceRoute 25 { 26 Address = new[] 27 { 28 new IpAddressModel {Ip = "127.0.0.1", Port = 9881} 29 }, 30 ServiceDescriptor = i.Descriptor 31 }); 32 var serviceRouteManager = buildServiceProvider.GetRequiredService<IServiceRouteManager>(); 33 serviceRouteManager.SetRoutesAsync(addressDescriptors).Wait(); 34 35 // 構建內部日志處理 36 buildServiceProvider.GetRequiredService<ILoggerFactory>().AddConsole((console, logLevel) => (int) logLevel >= 0); 37 38 // 獲取服務宿主 39 var serviceHost = buildServiceProvider.GetRequiredService<IServiceHost>(); 40 41 Task.Factory.StartNew(async () => 42 { 43 //啟動主機 44 await serviceHost.StartAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9881)); 45 }); 46 47 Console.ReadLine(); 48 } 全程基于serviceCollection實現自動裝配和構造,相信用過Ioc容器都能明白這上面幾條依賴注入和自動構建服務的含義。 再添加客戶端代碼: 1 static void Main() 2 { 3 var serviceCollection = new ServiceCollection(); 4 { 5 serviceCollection 6 .AddLogging() // 添加日志 7 .AddClient() // 添加客戶端 8 .UseSharedFileRouteManager(@"d:\routes.txt") // 添加共享路由 9 .UseDotNettyTransport(); // 添加DotNetty通信傳輸 10 } 11 12 var serviceProvider = serviceCollection.BuildServiceProvider(); 13 14 serviceProvider.GetRequiredService<ILoggerFactory>().AddConsole((console, logLevel) => (int) logLevel >= 0); 15 16 var services = serviceProvider.GetRequiredService<IServiceProxyGenerater>() 17 .GenerateProxys(new[] {typeof(IUserService)}).ToArray(); 18 19 var userService = serviceProvider.GetRequiredService<IServiceProxyFactory>().CreateProxy<IUserService>( 20 services.Single(typeof(IUserService).GetTypeInfo().IsAssignableFrom) 21 ); 22 23 while (true) 24 { 25 Task.Run(async () => 26 { 27 Console.WriteLine($"userService.GetUserName:{await userService.GetUserName(1)}"); 28 Console.WriteLine($"userService.GetUserId:{await userService.GetUserId("rabbit")}"); 29 Console.WriteLine($"userService.GetUserLastSignInTime:{await userService.GetUserLastSignInTime(1)}"); 30 var user = await userService.GetUser(1); 31 Console.WriteLine($"userService.GetUser:name={user.Name},age={user.Age}"); 32 Console.WriteLine($"userService.Update:{await userService.Update(1, user)}"); 33 Console.WriteLine($"userService.GetDictionary:{(await userService.GetDictionary())["key"]}"); 34 await userService.Try(); 35 Console.WriteLine("client function completed!"); 36 }).Wait(); 37 Console.ReadKey(); 38 } 39 }

    ?

    我想看到這里,明白上面代碼的作用,也就明白了這個框架的作用,客戶端能像調用本地方法一樣去調用遠程方法,并且中間過程是完全透明的,分離,分離,分離。 微服務的作用不再介紹,呵呵。 感謝閱讀!

    ?

    總結

    以上是生活随笔為你收集整理的NET Core微服务之路:让我们对上一个Demo通讯进行修改,完成RPC通讯的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 男人手机天堂 | 精品久久毛片 | 中文字幕在线免费播放 | 国产精品98| 亚洲久久久久久久 | 91桃色免费视频 | 久久久亚洲一区二区三区 | 香港三级日本三级三69 | 日本精品区 | 亚洲国产极品 | 一级黄大片 | 成人91免费 | 在线观看 中文字幕 | 青草久久网| 精品欧美一区二区三区 | 777精品视频 | 高潮无码精品色欲av午夜福利 | 欧美成人日韩 | 成人无码av片在线观看 | 久久综合亚洲精品 | 午夜老湿机 | www.国产91| 亚洲欧洲一区二区在线观看 | 男女激情四射网站 | 99免费国产 | 中文字幕一区二 | 草色噜噜噜av在线观看香蕉 | 成人网一区 | 国产一级一区二区 | 99色在线视频 | 少妇色欲网| www.av黄色| 国产高清免费av | 精品亚洲成人 | 国产精品乱码久久久久久 | 在线视频 亚洲 | 东京干手机福利视频 | 国产成年人免费视频 | 国产精品手机在线观看 | 法国性xxxx精品hd | 官场艳妇疯狂性关系 | 欧美日韩精品一区二区三区 | 无码免费一区二区三区免费播放 | 免费在线观看黄视频 | 中国免费毛片 | 美女赤身免费网站 | 国产高清精品软件丝瓜软件 | 亚洲天堂男人天堂 | 精品国产精品三级精品av网址 | 欧美性猛交乱大交 | 亚洲高清免费观看 | 亚洲av日韩精品久久久久久久 | 毛片高清 | 日韩网站免费观看高清 | 国产日韩在线看 | 蘑菇福利视频一区播放 | 国产一区久久 | xxxxxx国产 | 国产性一乱一性一伧一色 | 天天撸天天操 | 在线免费毛片 | 久久久精品中文字幕麻豆发布 | 日本亚洲综合 | 男女透逼视频 | 欧洲成人av | 中文字幕一区二区三区精华液 | 成人网av | 国产精品免费无遮挡无码永久视频 | 色戒未删节版 | 五月激情婷婷在线 | 国产精品中文久久久久久 | 成人小视频在线 | 三级av在线免费观看 | 国产丝袜一区二区三区 | 国产成人二区 | 久久久一二三四 | 中文字幕在线播放第一页 | 涩av| 99精品人妻无码专区在线视频区 | 国产污视频网站 | 国产在线免费av | av制服丝袜 | 国产精品91久久 | www狠狠操| av视觉盛宴 | 九九热精品视频在线 | 欧美激情在线 | 五月av | 精品一区二三区 | 99国产在线观看 | 丝袜五月天 | eeuss日韩 | 久久精品一区二区国产 | 一起艹在线观看 | 一区二区三区av在线 | 成人国产精品蜜柚视频 | 韩日视频| 国产热热 | 欧美大片xxx |