當前位置:
首頁 > 最新 > 基於google protobuf的gRPC實現

基於google protobuf的gRPC實現


1.Protobuf簡介

Protobuf(Google Protocol Buffers)提供一種靈活、高效、自動化的機制,用於序列化結構數據。Protobuf僅需自定義一次所需要的數據格式,然後我們就可以使用Protobuf編譯器自動生成各種語言的源碼,方便我們讀寫自定義的格式化數據。另外Protobuf的使用與平台和語言無關,可以在不破壞原數據格式的基礎上,擴展新的數據。

我們可以將Protobuf與XML進行對比,但Protobuf更小、更快、更加簡單。總結來說具有一下特點:

性能好、效率高。Protobuf作用與XML、json類似,但它是二進位格式,所以性能更好。但同時因為是二進位格式,所以缺點也就是可讀性差。

支持多種語言,例C++、C#、Go、Java、Python等。

代碼生成機制,易於使用。

向前兼容,向後兼容。

解析速度快。


Mac用戶可以使用brew進行安裝,命令如下所示。

如需要安裝特定版本,可以先進行搜索有哪些版本,命令如下所示。搜索完成之後,採用上述brew安裝方法,安裝特定版本即可。

安裝完成之後,可以通過protoc --version查看是否安裝成功。

另外可以通過which protoc命令查看protoc安裝所在的位置。

3.1編譯.proto文件

首先我們需要創建一個以.proto結尾的文件,可以在其中定義message來指定所需要序列化的數據格式。每一個message都是一個小的信息邏輯單元,包含一系列的name-value值對。以官網上的示例,我們創建一個addressbook.proto文件,內容如下所示。

syntax=」proto2」代表版本,目前支持proto2和proto3,不寫默認proto2。

package類似於C++中的namespace概念。

message是包含了各種類型欄位的聚集,相當於struct,並且可以嵌套。

proto3版本去掉了required和optional類型,保留了repeated(數組)。其中「=1」,「=2」表示每個元素的標識號,它會用在二進位編碼中對域的標識,[1,15]之內的標誌符在使用時佔用一個位元組,[16,2047]之內的標識號則佔用2個位元組,所以從最優化角度考慮,可以將[1,15]使用在一些較常用或repeated的元素上。同時為了考慮將來可能會增加新的標誌符,我們要事先預留一些標誌符。

構建好addressbook.proto文件後,運行Protobuf編譯器編譯.proto文件,運行方法如下所示。其中-I表示.protoc所在的路徑,--python_out表示指定生成的目標文件存在的路徑,最後的參數表示要編譯的.proto文件。

其中SRC_DIR為目錄,如果處於當前目錄的話,可通過如下所示命令來編譯.proto文件。

編譯完成之後會生成addressbook_pb2.py文件,裡面包含序列化和反序列化等方法。


創建add_person.py文件,代碼如上所示,然後通過SerializeToString()方法來進行序列化addressbook.proto中所定義的信息。如果想要運行上述代碼的話,我們首先需要創建一個輸入文件,例如命名為input.txt,不需輸入值。然後採用 ,便可進行序列化所輸入的數據。如果運行 的話,不指定輸入文件,則會報錯。


創建list_person.py文件來進行反序列化,代碼如上所示。通過 命令來執行上述代碼,輸出結果如下所示。


這裡引用知乎用戶用心閣關於誰能用通俗的語言解釋一下什麼是 RPC 框架?的問題答案來解釋什麼是RPC。RPC(Remote Procedure Call)是指遠程過程調用,也就是說兩台伺服器A、B,一個應用部署在A伺服器上,想要調用B伺服器上應用提供的函數/方法,由於不在一個內存空間上,不能直接調用,需要通過網路來表達調用的語義和傳達調用的數據。如果需要實現RPC,那麼需要解決如下幾個問題。

通訊:主要是通過在客戶端和伺服器之間建立TCP連接,遠程過程調用的所有交換的數據都在這個連接里傳輸。連接可以是按需連接,調用結束後就斷掉,也可以是長連接,多個遠程過程調用共享同一個連接。

定址:A伺服器上的應用怎麼告訴底層的RPC框架,如何連接到B伺服器(如主機或IP地址)以及特定的埠,方法的名稱名稱是什麼。

序列化:當A伺服器上的應用發起遠程過程調用時,方法的參數需要通過底層的網路協議,如TCP傳遞到B伺服器。由於網路協議是基於二進位的,內存中的參數值要序列化成二進位的形式,也就是序列化(Serialize)或編組(marshal),通過定址和傳輸將序列化的二進位發送給B伺服器。 B伺服器收到請求後,需要對參數進行反序列化,恢復為內存中的表達方式,然後找到對應的方法進行本地調用,然後得到返回值。 返回值還要發送回伺服器A上的應用,也要經過序列化的方式發送,伺服器A接到後,再反序列化,恢復為內存中的表達方式,交給A伺服器上的應用 。

總結來說,RPC提供一種透明調用機制讓使用者不必顯示區分本地調用還是遠程調用。如上圖所示,客戶方像調用本地方法一樣去調用遠程介面方法,RPC 框架提供介面的代理實現,實際的調用將委託給代理 。代理封裝調用信息並將調用轉交給 去實際執行。在客戶端的 通過連接器 去維持與服務端的通道 ,並使用 執行協議編碼(encode)並將編碼後的請求消息通過通道發送給服務方。RPC 服務端接收器 接收客戶端的調用請求,同樣使用 執行協議解碼(decode)。解碼後的調用信息傳遞給 去控制處理調用過程,最後再委託調用給 去實際執行並返回調用結果。


我們可以利用protobuf實現序列化和反序列化,但如何實現RPC通信呢。為簡單起見,我們先介紹gRPC,gRPC是google構建的RPC框架,這樣我們就不再考慮如何寫通信方法。

5.1gRPC安裝

首先安裝gRPC,安裝命令如下所示。

然後安裝protobuf相關的依賴庫。

然後安裝python gRPC相關的protobuf相關文件。

5.2gRPC實例

創建三個文件夾,名稱為example、server、client,裡面內容如下所示,具體含義在後面解釋。

5.2.1 example

example主要用於編寫.proto文件並生成data介面,其中__init__.py的作用是方便其他文件夾引用example文件夾中文件,data.proto文件內容如下所示。

然後在example目錄下利用下述命令生成data_pb2.py和data_pb2_grpc.py文件。data_pb2.py用於序列化信息,data_pb2_grpc.py用於通信。

5.2.2 server

server為伺服器端,server.py實現接受客戶端發送的數據,並對數據進行處理後返回給客戶端。FormatData的作用是將伺服器端傳過來的數據轉換為大寫,具體含義見相關代碼和注釋。

5.2.3 client

clinet為客戶端,client.py實現客戶端發送數據,並接受server處理後返回的數據,具體含義見相關代碼和注釋。

接下來運行server.py來啟動伺服器,然後運行client.py便可以得到結果,可以看到所有數據均已大寫。最後需要關閉伺服器端,否則一直會處於運行狀態。


因為RPC需要我們實現通信,所以會有一定難度,代碼量很大程度上也有增加,不方便在文中展現出來。所以我把代碼放到了github上面,地址在https://github.com/weizhixiaoyi/google-protobuf-service,有興趣的可以看下。

總的來說,protobuf RPC定義了一個抽象的RPC框架,RpcServiceStub和RpcService類是protobuf編譯器根據proto定義生成的類,RpcService定義了服務端暴露給客戶端的函數介面,具體實現需要用戶自己繼承這個類來實現。RpcServiceStub定義了服務端暴露函數的描述,並將客戶端對RpcServiceStub中函數的調用統一轉換到調用RpcChannel中的CallMethod方法,CallMethod通過RpcServiceStub傳過來的函數描述符和函數參數對該次rpc調用進行encode,最終通過RpcConnecor發送給服務方。對方以客戶端相反的過程最終調用RpcSerivice中定義的函數。

事實上,protobuf rpc的框架只是RpcChannel中定義了空的CallMethod,所以具體怎樣進行encode和調用RpcConnector都要自己實現。RpcConnector在protobuf中沒有定義,所以這個完成由用戶自己實現,它的作用就是收發rpc消息包。在服務端,RpcChannel通過調用RpcService中的CallMethod來具體調用RpcService中暴露給客戶端的函數。

參考

用心閣-誰能用通俗的語言解釋一下什麼是 RPC 框架?

在於思考-python通過protobuf實現rpc

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 謂之小一 的精彩文章:

TAG:謂之小一 |