goでgRPCはやったことあったけどpythonではどうやるんだろうーと思ったので、チュートリアルにチャレンジin mac
goのクイックスタートをやってる方もいてとても分かりやすくまとまっています
https://budougumi0617.github.io/2018/01/01/hello-grpc-go/
間違いあったら指摘いただけると助かります
インストール
# grpcのインストール
pip install --upgrade pip
sudo python -m pip install grpcio
sudo python -m pip install grpcio-tools
# サンプルコードのインストール
git clone -b v1.18.0 https://github.com/grpc/grpc
動くことを確認
cd grpc/examples/python/helloworld
python greeter_server.py
# 別ターミナルで
python greeter_client.py
Greeter client received: Hello, you!
# 出たね
protoファイルからスクリプトを生成
protoファイルを作る
// vim my_if.proto
// なにかをするservice(xx_pb2_proto.pyのコメントとして使用される)
service MyGrpc {
// なにかを受け取るRPC(xx_pb2_proto.pyのコメントとして使用される)
rpc GetSomething (MyReq) returns (MyResp) {}
}
message MyReq {
int32 int_param = 1;
string str_param = 2;
}
message MyResp {
int32 status = 1;
string message = 2;
}
コマンドで生成
python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. ./my_if.proto
(いちいちコマンド打つのは面倒いのでこちらの記事を参考に設定ファイルで作成するようにしました)
my_if_pb2.py
とmy_if_pb2_grpc.py
が生成される。(pb2
の2
はProtocol Buffers Python APIのバージョンを示していて1
と互換性がない)
内容は以下:
- classes for the messages defined in route_guide.proto
- classes for the service defined in route_guide.proto
- RouteGuideStub, which can be used by clients to invoke RouteGuide RPCs
- RouteGuideServicer, which defines the interface for implementations of the RouteGuide service
- a function for the service defined in route_guide.proto
- add_RouteGuideServicer_to_server, which adds a RouteGuideServicer to a grpc.Server
自分用に読み換えると以下のような雰囲気(たぶん)
- messages用クラス
- service用クラス
-
MyGrpcStub
はMyGrpcを呼び出すのに使用するクライアント -
MyGrpcServicer
はserviceを実装する用のインターフェース
-
- service用のfunc
-
add_MyGrpcServicer_to_server
はgrpcサーバにMyGrpcServicer
を追加する
-
とりあえずインストールからスクリプト生成まで完了
サーバを作ろう
サンプルを見るのが分かりやすい。
生成されたxx_grpc.pyとかを見ながら実装します。全ソースはgithubに。
# server.py
from concurrent import futures
import time
import grpc
from proto import my_if_pb2
from proto import my_if_pb2_grpc
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
# my_if_pb2_grpcのMyGrpcServicer()を実装
class MyGrpc(my_if_pb2_grpc.MyGrpcServicer):
def GetSomething(self, request, context):
# いろんな処理はここで
print("Someone requested something!")
# データ取得は`request.xxx`でできて分かりやすい
print(f'int_param: {request.int_param}')
print(f'str_param: {request.str_param}')
# 必ず設定したresponseを返却する
response = my_if_pb2.MyResp(status=200, message="Great message.")
return response
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# MyGrpc()を使うよ!と登録しているかんじ
my_if_pb2_grpc.add_MyGrpcServicer_to_server(MyGrpc(), server)
# portの設定
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
print("start")
serve()
サンプルとだいたい一緒になりました(シンプルなことしかしてないので当然だけど)
/code/grpc_practice
❯ python server.py
start
疎通確認できてませんがとりあえず起動はおーけー
クライアントを作ろう
import grpc
from proto import my_if_pb2
from proto import my_if_pb2_grpc
def run():
# SSL/TLS認証を利用する場合は`secure_channel`を使用する
# 通信先を設定する(server.pyでポートを50051に設定したのでそれを指定)
# リトライ設定等はoptionで指定できる
with grpc.insecure_channel('localhost:50051') as channel:
stub = my_if_pb2_grpc.MyGrpcStub(channel)
# ここでデータを送信している
resp = stub.GetSomething(my_if_pb2.MyReq(int_param=99, str_param='me'))
# 受け取ったデータはxx.yyで受け取れる
print(f'client received: status={resp.status}, message={resp.message}')
if __name__ == '__main__':
print("run")
run()
簡単です!
実際に送信してみる
1. まずサーバを実行して待っていてもらう
2. クライアントを実行
/code/grpc_practice
❯ python client.py
run
client received: status=200, message=Great message.
# サーバで設定していたstatusとmessageを受け取れました
3. サーバのログを確認
/code/grpc_practice
❯ python server.py
start
Someone requested something!
int_param: 99
str_param: me
# クライアントから送信したパラメータを受け取れました
できましたー