Asp-Net-Core學習筆記:gRPC快速入門

2023-07-12 06:00:46

前言

此前,我在做跨語言呼叫時,用的是 Facebook 的 Thrift,挺輕量的,還不錯。

Thrift是一種介面描述語言和二進位制通訊協定,它被用來定義和建立跨語言的服務。它被當作一個遠端過程呼叫(RPC)框架來使用,是由Facebook為「大規模跨語言服務開發」而開發的。它通過一個程式碼生成引擎聯合了一個軟體棧,來建立不同程度的、無縫的跨平臺高效服務,可以使用C#、C++(基於POSIX相容系統)Cappuccino、Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk程式語言開發。 2007由Facebook開源,2008年5月進入Apache孵化器, 2010年10月成為Apache的頂級專案。

最近的專案中也有類似的需求,這次打算使用 Google 的 gRPC,原因是 gRPC 知名度也很高,之前一直想用但沒有場景,加上 AspNetCore 的檔案裡有介紹,看起來官方也是推薦使用這種方式進行 RPC 呼叫。

所以就果斷開始了 gRPC 實踐,最終做出來的效果還是可以的(這是後話)。

本文主要介紹 C# 與 Python 之間,使用 gRPC 進行跨語言呼叫。

概念科普

什麼是RPC?

遠端過程呼叫(Remote Procedure Call,縮寫為 RPC)是一個計算機通訊協定。該協定允許執行於一臺計算機的程式呼叫另一臺計算機的子程式,而程式設計師無需額外地為這個互動作用程式設計。 比如 Java RMI(遠端方法呼叫(Remote
Method Invocation)。能夠讓在某個java虛擬機器器上的物件像呼叫本地物件一樣呼叫另一個java虛擬機器器中的物件上的方法)。

從上圖可以看出, RPC 本身是 client-server模型,也是一種 request-response 協定。
有些實現擴充套件了遠端呼叫的模型,實現了雙向的服務呼叫,但是不管怎樣,呼叫過程還是由一個使用者端發起,伺服器端提供響應,基本模型沒有變化。
服務的呼叫過程為:

  1. client呼叫client stub,這是一次本地過程呼叫
  2. client stub將引數打包成一個訊息,然後傳送這個訊息。打包過程也叫做 marshalling
  3. client所在的系統將訊息傳送給server
  4. server的的系統將收到的包傳給server stub
  5. server stub解包得到引數。 解包也被稱作 unmarshalling
  6. 最後server stub呼叫服務過程. 返回結果按照相反的步驟傳給client

關於 gRPC

gRPC 是一種與語言無關的高效能遠端過程呼叫 (RPC) 框架。

前文有說過,這個是 Google 開發的,以下是 AspNetCore 的檔案中,對 gRPC 的介紹。

gRPC 的主要優點是:

  • 現代高效能輕量級 RPC 框架。
  • 協定優先 API 開發,預設使用協定緩衝區,允許與語言無關的實現。
  • 可用於多種語言的工具,以生成強型別伺服器和使用者端。
  • 支援使用者端、伺服器和雙向流式處理呼叫。
  • 使用 Protobuf 二進位制序列化減少對網路的使用。

這些優點使 gRPC 適用於:

  • 效率至關重要的輕量級微服務。
  • 需要多種語言用於開發的 Polyglot 系統。
  • 需要處理流式處理請求或響應的對等實時服務。

gRPC 使用 .proto 檔案來定義介面和資料型別,同時提供了通過 .proto 檔案生成不同語言呼叫程式碼的工具。

專案介紹

本文的專案是使用 C# 呼叫 Python 寫的服務,這個服務是一個大語言模型。

在本專案中,C# 專案作為使用者端,Python 專案作為伺服器端。

編寫 .proto 檔案

第一步,要先編寫用於定義介面和資料結構的 .proto 檔案。

將下面程式碼儲存到 chat.proto 檔案裡,接下來在要接入 gRPC 的每個專案裡,都要複製一份這個檔案。

syntax = "proto3";

import "google/protobuf/wrappers.proto";

option csharp_namespace = "AIHub.Blazor";

package aihub;

service ChatHub {
  rpc Chat (ChatRequest) returns (ChatReply);
  rpc StreamingChat (ChatRequest) returns (stream ChatReply);
}

message ChatRequest {
  string prompt = 1;
  repeated google.protobuf.StringValue history = 2;
  int32 max_length = 3;
  float top_p = 4;
  float temperature = 5;
}

message ChatReply {
  string response = 1;
}

這裡定義了兩個 Chat 方法,其中一個是一元呼叫,一個是流式輸出。

gRPC 服務可以有不同型別的方法,服務傳送和接收訊息的方式取決於所定義的方法的型別

  • 一元 - 呼叫完就返回,同步方法
  • 伺服器流式處理 - 伺服器端流式返回資料
  • 使用者端流式處理 - 使用者端流式寫入資料
  • 雙向流式處理

都挺好理解的,在 proto 定義裡面也很易懂,stream 放在哪裡,哪裡就是流式。

stream 放在請求引數,那使用者端輸入就是流式的,放在返回值前面,那伺服器端的返回就是流式。

都放就代表雙向流式。

接著又定義了用到的資料結構,一個輸入引數 ChatRequest,一個返回引數 ChatReply

string prompt = 1; 這裡賦值的意思是這個引數的位置,不是定義變數的賦值