0x00 php.cn/code/10224.html" target="_blank">簡介
DotBPE.RPC是一款基於dotnet core編寫的RPC架構,而它的爸爸DotBPE,目標是實現一個開箱即用的微服務架構,但是它還差點意思,還僅僅在構思和嘗試的階段。但不管怎麼說RPC是微服務的基礎,先來講講RPC的實現吧。DotBPE.RPC底層通訊預設實現基於DotNetty,這是由微軟Azure團隊開發的C#的Netty實現,非常酷。當然你也可以替換成其他Socket通訊組件。DotBPE.RPC使用的預設協議名稱叫Amp,編解碼使用Google的Protobuf3,不過這些預設實現都是可以替換的。
源碼地址:github.com/xuanye/dotbpe.git
0x01 關於Amp協議和Google Protobuf
Amp(A Message Protocol)
Amp(A Message Protocol) ,中文名叫 一個訊息協議
,是DotBPE.RPC預設實現的訊息協議,在實際開發中,其實是不需要瞭解訊息是如何編解碼和傳輸的,但是瞭解協議有助於進一步瞭解架構。協議基本結構如所示:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 <length>-14+------------+----------+---------+------+-------+---------+------------+| <ver/argc> | <length> | <seq> |<type>|<serId>| <msgid> | <data> |+------------+----------+---------+------+-------+---------+------------+
Amp協議預設訊息頭長為14個位元組,不包含擴充包頭
第0位:ver/argc // 為版本號碼,暫時來說,預設為0
第1-4位: length //為包總長度(含包頭長度)
第5-8位: sequence // 為訊息序號,通過該序號對應 請求<--->響應
第9位: type // 訊息類型,現值有5種,如下:
Request = 1, Response = 2, Notify = 3,NotFound = 4, ERROR = 5
第10-11位: serviceId//訊息ID ushort類型
第12-13位: msgId//訊息ID ushort類型
在Amp協議中,serviceId標識一類請求,類似應用中的模組,而msgId標識模組中的具體方法
其後緊跟實際的資料
Google Protobuf
Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言資料標準,目前已經正在使用的有超過 48,162 種報文格式定義和超過 12,183 個 .proto 檔案。他們用於 RPC 系統和持續資料存放區系統。
Protocol Buffers 是一種輕便高效的結構化資料存放區格式,可以用於結構化資料序列化,或者說序列化。它很適合做資料存放區或 RPC 資料交換格式。可用於通訊協議、資料存放區等領域的語言無關、平台無關、可擴充的序列化結構資料格式。目前提供了 多種語言的API,包括C++、 C# 、GO、 JAVA、 PYTHON
我在之前的部落格使用CSharp編寫Google Protobuf外掛程式中有過介紹如果通過編寫外掛程式的方式,來通過定義proto檔案,並產生我們需要的代碼。
在DotBPE.RPC 中,我使用protobuf來作為服務的描述檔案,並通過自訂的外掛程式方式來產生服務端和用戶端代理類。
0x02 快速開始
0. 前提
因為DotBPE是基於dotnet core開發的,你本地必須已經有了dotnet core開發環境
使用githubManaged 程式碼,所以你必須已經安裝了git用戶端
需要通過protoc產生模板代碼,所以你必須已經安裝了google protobuf命令列工具
1. 下載樣本程式
為了能夠解釋我們的快速開始程式,你需要一份本地能夠啟動並執行範例程式碼。從github上下載我已經寫好的範例程式碼,可以讓你快速的搭建程式,免去一些繁瑣,但是又必須的步驟。
>$ # Clone the repository to get the example code: >$ git clone https://github.com/xuanye/dotbpe-sample.git >$ cd dotbpe-sample
使用VS2017,或者VSCode開啟下載好的代碼,目錄結構如下所示:
如果你使用VS2017 可以自動幫你還原,如果使用VSCode的話 ,需要運行dotnet restore
下載依賴,成功後使用dotnet build
編譯一下看看結果:看著很完美
2. 運行程式
運行Server
>$ cd HelloDotBPE.Server >$ dotnet run
運行Client
>$ cd HelloDotBPE.Client >$ dotnet run
恭喜!已經使用DotBPE.RPC運行一個Server/Client的應用程式。
3. 來一起看一下代碼吧
3.1 服務描述檔案proto
首先是DotBPE.RPC架構中對proto的擴充檔案,所有的項目都需要這個檔案,關於如何擴充proto,我的這篇部落格有比較詳細的介紹,這裡就不重複說了
//dotbpe_option.proto 檔案syntax = "proto3";package dotbpe;option csharp_namespace = "DotBPE.ProtoBuf";import "google/protobuf/descriptor.proto";//擴充服務extend google.protobuf.ServiceOptions { int32 service_id = 51001; bool disable_generic_service_client = 51003; //禁止產生用戶端代碼 bool disable_generic_service_server = 51004; //禁止產生服務端代碼}extend google.protobuf.MethodOptions { int32 message_id = 51002;}extend google.protobuf.FileOptions { bool disable_generic_services_client = 51003; //禁止產生用戶端代碼 bool disable_generic_services_server = 51004; //禁止產生服務端代碼 bool generic_markdown_doc = 51005; //是否產生文檔 本樣本中無用 bool generic_objectfactory = 51006; //是否產生objectfactory 本樣本中無用}
下面的服務描述檔案 greeter.proto
才是真正的樣本的服務描述檔案:比較簡單,定義一個Greeter Rpc服務,並定義一個Hello的方法
//greeter.protosyntax = "proto3";package dotbpe;option csharp_namespace = "HelloDotBPE.Common";// 引入擴充import public "dotbpe_option.proto";// 定義一個服務service Greeter { option (service_id)= 100 ;//訊息ID,全域必須唯一 // Sends a greeting rpc Hello (HelloRequest) returns (HelloResponse) { option (message_id)= 1 ;//設定訊息ID,同一服務內唯一 }}// The request message containing the user's name.message HelloRequest { string name = 1;}// The response message containing the greetingsmessage HelloResponse { string message = 1;}
通過protoc工具產生模板代碼,樣本中的代碼產生到了 HelloDotBPE.Common_g 目錄下,本地可以運行shell命令的同學可以直接到
dotbpe-sample\script\generate 目錄運行sh generate_hello.sh
(windows下一般安裝cgywin),不能啟動並執行同學也可以在HelloDotBPE目錄下,直接運行命令列
protoc -I=../protos --csharp_out=./HelloDotBPE.Common/_g/ --dotbpe_out=./HelloDotBPE.Common/_g/ ../protos/dotbpe_option.proto ../protos/greeter.proto --plugin=protoc-gen-dotbpe=../../tool/protoc_plugin/Protobuf.Gen.exe
當然我還是建議大家安裝以下cgywin運行環境,可以運行unix上的一些常用命令。同時在部署到正式環境的時候可以公用開發環境的一些指令碼。
3.2 服務端代碼
服務實現:
// 服務實現代碼public class GreeterImpl : GreeterBase { public override Task<HelloResponse> HelloAsync(HelloRequest request) { // 直接返回Hello Name return Task.FromResult(new HelloResponse() { Message = "Hello " + request.Name }); }}
服務端啟動類
public class Startup : IStartup { public void Configure(IAppBuilder app, IHostingEnvironment env) { } public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddDotBPE(); // 添加DotBPE.RPC的核心依賴 services.AddServiceActors<AmpMessage>(actors => { actors.Add<GreeterImpl>(); // 註冊服務實現 }); return services.BuildServiceProvider(); } }
啟動服務端
class Program { static void Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; //在控制台輸出調試日誌 DotBPE.Rpc.Environment.SetLogger(new DotBPE.Rpc.Logging.ConsoleLogger()); var host = new RpcHostBuilder() .UseServer("0.0.0.0:6201") //綁定本地連接埠6201 .UseStartup<Startup>() .Build(); host.StartAsync().Wait(); Console.WriteLine("Press any key to quit!"); Console.ReadKey(); host.ShutdownAsync().Wait(); } }
3.3 用戶端代碼
class Program { static void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; var client = AmpClient.Create("127.0.0.1:6201"); //建立連結通道 var greeter = new GreeterClient(client); //用戶端代理類 while (true) { Console.WriteLine("input your name and press enter:"); string name = Console.ReadLine(); if ("bye".Equals(name)) { break; } try { var request = new HelloRequest() { Name = name }; var result = greeter.HelloAsync(request).Result; Console.WriteLine($"---------------receive form server:{result.Message}-----------"); } catch (Exception ex) { Console.WriteLine("發生錯誤:" + ex.Message); } } Console.WriteLine($"---------------close connection-----------"); client.CloseAsync(); } }
0x03 下一步
下一篇 我將詳細講述DotBPE.RPC中的主要類和調用關係,並介紹如何使用DotNetty實現RPC通訊。
事實上我正在編寫一個更加複雜的樣本https://github.com/xuanye/PiggyMetrics.git,
這原是spring cloud的一個樣本程式,我使用DotBPE進行改造,用樣本描述DotBPE在真實情境中的應用。包括服務註冊和發現,服務間調用,公開HttpApi,監控檢查等功能,並通過實踐進一步完善DotBPE。初步的功能已經實現,不過還沒來的及寫文檔。該系列的後面將詳細描述該系統的實現。