建構流
gRPC 利用
HTTP/2
的雙向通信特性實作了
連續的消息交換
,實作了
雙向流
。
資料流是什麼?
流資料有各種各樣的場景用法
。一種是,
當事件發生時,有一種方法可以不斷發出描述事件的消息
。例如,當資料被添加到資料庫中時,資料庫希望将“資料添加”事件通知相關方。或者當股票價格發生變化時,證券交易所希望向訂閱“價格變化”事件的所有服務公布新價格。
另一個是
有一種以異步方式高效傳輸非常大的資料集的方法
。例如,假設您有一個傳回一百萬條記錄的查詢,其中每條記錄對調用者都有值。能夠在每條記錄以流的方式進入時檢查它,比等到所有100萬條記錄都收到後再批量處理它們要有效得多。或者,想象你有一個電視控制台,想要攝入一部電影5分鐘的時間來處理。通過流消費和處理5分鐘的片段意味着消費者可以在電影進入時持續觀看,而不必等到整部電影下載下傳後才能觀看。
環境準備
- python 3.7
- pip 最新版, 可以用下面的指令更新
python -m pip install --upgrade pip
複制
安裝
gRPC
python -m pip install grpcio
複制
這個還需要安裝一個
gRPC tools
, Python 的 gRPC 工具包括協定緩沖編譯器 protoc 和用于從 .proto服務定義。
python -m pip install grpcio-tools
複制
為了學習,這個提供要給demo
git clone -b v1.33.1 https://github.com/grpc/grpc
# 國内可以通路這個
git clone [email protected]:chasays/grpc.git
複制
clone 之前需要安裝 protoc 和 grpc_python_plugin
- protoc 可以直接用
, 如果是其他的類似,或者下載下傳protoc,然後解壓後放到環境裡面brew install protoc
- grpc_python_plugin 這個在上面clone的工程裡面,編譯
然後把編譯好的檔案copy到PATH裡面即可make grpc_python_plugin
(base) ➜ grpc git:(master) ✗ make grpc_python_plugin
[HOSTCXX] Compiling src/compiler/cpp_generator.cc
[HOSTCXX] Compiling src/compiler/csharp_generator.cc
[HOSTCXX] Compiling src/compiler/objective_c_generator.cc
[HOSTCXX] Compiling src/compiler/python_generator.cc
[HOSTCXX] Compiling src/compiler/ruby_generator.cc
[AR] Creating /Users/admin/Documents/OpenSource/grpc/libs/opt/libgrpc_plugin_support.a
[HOSTCXX] Compiling src/compiler/python_plugin.cc
[HOSTLD] Linking /Users/admin/Documents/OpenSource/grpc/bins/opt/grpc_python_plugin
(base) ➜ sudo cp bins/opt/grpc_python_plugin /usr/local/bin/
複制
先來看一個最簡單的 helloworld
然後切換目錄到
cd grpc/examples/python/helloworld
執行
run_codegen.sh
,即可生成
helloworld_pb2.py
檔案。
然後依次執行
greeter_server.py
和
greeter_client.py
。 就可以看到輸出
python greeter_server.py
# 再開一個,shell程序執行
python greeter_client.py
# 需要注意執行client的時候一定要用python2, 用py3需要修改下檔案裡面print這句
(base) ➜ helloworld git:(master) ✗ python2 greeter_client.py
Greeter client received: Hello, you!
複制
這個裡面有個
stub
,需要提一下, 網上看了下,這個解釋是不錯的。
寫碼的時候你會遇到一些外部依賴,比如在本機上寫代碼,可能會調用谷歌的API,來完成遠端調用。而我在做測試的時候并不想真的發出這個請求,(貴,得不到想要的結果),是以我選擇通過某種方式(Mockito)來進行模拟。Stub指的就是這種模拟,把服務端的依賴用本機來進行模拟
也可以用
Bloomrpc
導入 protoc檔案,然後直接執行。
注意用這個執行之前需要啟動 server
。
Streaming
要定義一個服務,你需要在你的. proto 檔案中指定一個命名的服務:
service RouteGuide {
// (Method definitions not shown)
}
複制
然後在服務定義中定義 rpc 方法,指定它們的請求和響應類型。讓你定義
四種
服務方法,所有這些都在 RouteGuide 服務中使用:
- 一個簡單的 RPC,其中用戶端使用存根向伺服器發送請求,并等待響應傳回,就像
。普通的函數調用一樣
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
複制
- 一種
RPC,響應流
。用戶端從傳回的流中讀取,直到沒有更多的消息。正如您在示例中看到的,您通過将 stream 關鍵字放在 response 類型之前來指定 response-streaming 方法。其中用戶端向伺服器發送請求,并獲得一個流來讀取一系列消息
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
複制
- 一種
RPC,其中客戶機寫入一請求流式
,它就會等待伺服器讀取所有消息并傳回響應。通過将 stream 關鍵字放在請求類型之前,可以指定請求流方法。系列消息并将它們發送到伺服器,同樣使用提供的流。一旦用戶端完成了消息的寫入
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
複制
- 一種
RPC,其中雙向流式
,是以用戶端和伺服器可以按照自己喜歡的順序讀寫: 例如,伺服器可以等待接收所有用戶端消息後再寫響應,或者可以交替讀取消息然後寫入消息,或者其他讀寫組合。保留了每個流中消息的順序。通過将 stream 關鍵字放在請求和響應之前,可以指定這種類型的方法。雙方使用讀寫流發送一系列消息。這兩個流獨立運作
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
複制
然後用 如下指令生成 python代碼。
$ python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/route_guide.proto
複制
建構server和client的代碼略。
https://github.com/grpc/grpc/blob/v1.33.1/examples/python/route_guide/route_guide_server.py
https://github.com/grpc/grpc/blob/v1.33.1/examples/python/route_guide/route_guide_client.py
複制
啟動server, 然後 push data,就可以在response看到對應的消息。
用protobuf 實作序列化和反序列化
用python來舉例吧,比如序列化就是request,用
SerializeToString
, 反序列化就用
FromString
。
request_serializers = {
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloRequest.SerializeToString,
}
response_deserializers = {
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloReply.FromString,
}
複制