DotBPE.RPC是一款基於dotnet core編寫的RPC框架,而它的爸爸DotBPE,目標是實現一個開箱即用的微服務框架,但是它還差點意思,還僅僅在構思和嘗試的階段。但不管怎麼說RPC是微服務的基礎,先來講講RPC的實現吧。DotBPE.RPC底層通信預設實現基於[DotNetty](htt... ...
0x00 簡介
DotBPE.RPC是一款基於dotnet core編寫的RPC框架,而它的爸爸DotBPE,目標是實現一個開箱即用的微服務框架,但是它還差點意思,還僅僅在構思和嘗試的階段。但不管怎麼說RPC是微服務的基礎,先來講講RPC的實現吧。DotBPE.RPC底層通信預設實現基於DotNetty,這是由微軟Azure團隊開發的C#的Netty實現,非常酷。當然你也可以替換成其他Socket通信組件。DotBPE.RPC使用的預設協議名稱叫Amp,編解碼使用谷歌的Protobuf3,不過這些預設實現都是可以替換的。
源碼地址:https://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開發環境
使用github托管代碼,所以你必須已經安裝了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.proto
syntax = "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 greetings
message 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。初步的功能已經實現,不過還沒來的及寫文檔。該系列的後面將詳細描述該系統的實現。