天天看點

使用命名管道承載gRPC

最近GRPC很火,感覺整RPC不用GRPC都快跟不上時髦了。

gRPC設計

gRPC是一種與語言無關的高性能遠端過程調用 (RPC) 架構。剛好需要使用一個的RPC應用系統,自然而然就盯上了它,但是它真能夠解決所有問題嗎?不見得,先看看他的優點:

gRPC的主要優點:

  • 現代高性能輕量級 RPC 架構。
  • 協定優先 API 開發,預設使用協定緩沖區,允許與語言無關的實作。
  • 可用于多種語言的工具,以生成強類型伺服器和用戶端。
  • 支援用戶端、伺服器和雙向流式處理調用。
  • 使用

    Protobuf

    二進制序列化減少對網絡的使用。

對應的适用場景:

  • 微服務:gRPC 設計用于低延遲和高吞吐量通信。 gRPC 對于效率至關重要的輕量級微服務非常有用。
  • 點對點實時通信:gRPC 對雙向流式傳輸提供出色的支援。 gRPC 服務可以實時推送消息而無需輪詢。
  • 多語言環境:gRPC 工具支援所有常用的開發語言,是以,gRPC 是多語言環境的理想選擇。
  • 網絡受限環境:gRPC 消息使用 Protobuf(一種輕量級消息格式)進行序列化。 gRPC 消息始終小于等效的 JSON 消息。

gRPC還是有缺點的:

  • 浏覽器支援受限:絕大數浏覽器不支援

    HTTP/2

  • 非人工可讀取:proto檔案規定的格式在通訊中會序列化成二進制資料,人工解析較為困難。

不适用的場景與替代:

  • 浏覽器可通路的API:gRPC 在浏覽器中未受到完全支援。

    gRPC-Web

    可以提供浏覽器支援,但它具有局限性并引入了伺服器代理。
  • 廣播實時通信:gRPC 支援通過流式傳輸進行實時通信,但不存在将消息廣播到注冊連接配接的概念。 例如,在聊天室方案中,應将新的聊天消息發送到聊天室中的所有用戶端,這要求每個 gRPC 調用将新的聊天消息單獨流式傳輸到用戶端。 SignalR 是适用于此方案的架構。 SignalR 具有持久性連接配接的概念,并内置對廣播消息的支援。
  • 程序間通信:程序必須托管 HTTP/2 伺服器才能接受傳入的 gRPC 調用。 對于 Windows,程序間通信管道是一種快速、輕便的通信方法。

目标分析

我需要有一個能夠實作遠端調用的好辦法,系統支援Windows就好,最好性能高一些(資料量大),程式小一點,但是我也不想直接處理二進制資料流(最好能有封裝的架構)。

考慮程序通信常用的:

  • 信号/信号量:簡單,能夠承載的消息内容較少。
  • 消息隊列:支援消息,功能較為強大。
  • 共享記憶體:性能最強,但隻限于單機。
  • 管道:性能較強,但是隻支援stream。
  • Socket:最靈活,但是需要有網卡。

首先排除信号/信号量,處理的資訊量太小了;然後共享記憶體也排除,隻能單機不符合我的要求;剩下的三個似乎都可以滿足要求,可以在這個基礎上建立RPC,而gRPC就是建立在socket(HTTP/2)上的,就像上面講的,要自己內建一個HTTP/2伺服器(比如Kestrel)才行,不夠輕量化;剩下的兩個Windows都有內建支援,可以考慮一下。

本着拿來主義的思想,我在github上找到一個grpc-dotnet-namedpipes,支援在命名管道上實作gRPC,相當于在stream上封裝了一層,不用直接處理二進制資料流了。

用作者自己的話來說,這麼做相較于普通的gRPC有幾個優點:

  • 更優秀的通路控制;
  • 純.NET庫,不需要帶ASP.NET Core或者超過3MB的非托管庫依賴;
  • 啟動時間更快
  • 2-3倍的資料吞吐量
  • 沒有防火牆警告
  • 不需要網卡

實作

建立一個proto

  1. 建立一個.NET Library
  2. 添加一個proto檔案,可以直接使用微軟的簡單例子
syntax = "proto3";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
           
  1. 添加nuget包:

    Google.Protobuf

    ,

    Grpc.Core

    Grpc.Tools

  2. 單擊proto檔案,在屬性對話框,選擇生成操作為Protobuf Compiler

建立Client

建立一個Console程式,添加上面的項目引用,輸入以下代碼:

var server = new NamedPipeServer("MY_PIPE_NAME");
Greeter.BindService(server.ServiceBinder, new GreeterService());
server.Start();
           

添加

GrpcDotNetNamedPipes

的nuget依賴:

Install-Package GrpcDotNetNamedPipes
           

建立Server

再建立一個Console程式,添加上面的項目引用,也添加那個nuget依賴和一些别的依賴,輸入以下代碼:

var channel = new NamedPipeChannel(".", "MY_PIPE_NAME");
var client = new Greeter.GreeterClient(channel);

var response = await client.SayHelloAsync(
	new HelloRequest { Name = "World" });

Console.WriteLine(response.Message);
           

然後運作就能看見熟悉的Hello World了,用起來和gRPC的标準實作沒太大差別。

總結

完整代碼見gRPC_Demo。

這種方式也有它的局限性,首先是Windows的命名管道與Linux上面的實作是不同的,是以并不能實作直接跨平台通訊;然後就是這個對于其他語言的開發的gRPC也不是完全相容的,需要其他語言開發的程式也做命名管道的适配才行,換言之,它不是通用标準。是以,對于一般的gRPC應用,還是更推薦使用标準實作。

參考

  • .NET Core 上的 gRPC 的簡介
  • 比較 gRPC 服務和 HTTP API
  • 關于IPC性能的提問
  • Linux上各種IPC性能比較

除非特殊說明,本作品由podolski創作,采用知識共享署名 4.0 國際許可協定進行許可。歡迎轉載,轉載請保留原文連結~喜歡的觀衆老爺們可以點下關注或者推薦~

繼續閱讀