この記事ではgRPC公式サイトのQuickStartを実践しながらgRPCの紹介を行なっていきます。
環境
名前 | バージョン |
---|---|
OS | CentOS Linux release 7.6.1810 (Core) |
Python | v2.7.5 |
前提
- 環境で提示したLinux環境を用意してください
【ハンズオン】gRPCの起動
pipのインストール
まずはPythonのライブラリを管理するpipツールをいれましょう。
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py chmod +x get-pip.py ./get-pip.py python -m pip install --upgrade pip
gRPCライブラリのインストール
つづいてgRPCを使うためのライブラリをいれます。
python -m pip install grpcio
gitのインストール
yum -y install git
grpcのサンプルのダウンロード
git clone -b v1.28.1 https://github.com/grpc/grpc
gRPCアプリの起動
通信をするために仮想端末(Teratermなど)を2つ用意し、以下のコマンドで移動してください。
cd grpc/examples/python/helloworld
- Server側
python greeter_server.py
- Client側
python greeter_client.py
成功するとClient側にて以下のような表示になります。
client# python greeter_client.py Greeter client received: Hello, you!
ここまできたらServer側のプロセスをCtrl+c
で終了してください。
まだ何がおきたのかわからなくて大丈夫です。次に行きましょう。
【ハンズオン】gRPCの更新
gRPCのprotoの修正
続いてgRPCで呼び出される内容を更新していきます。proto
と呼ばれるファイルで規定されていますので修正しましょう。
まずはexamples/protos/helloworld.proto
を以下のように変更してください。
〜
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
+ rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
〜
protoファイルの更新反映
続いて今変更したprotoファイル
を反映するためにコマンドを実行します。
examples/python/helloworld
に移動して以下のコマンドを叩いてください。
python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/helloworld.proto
先ほど作ったhelloworld.proto
が読み込まれ、リクエスト・レスポンスのクラスを含むhelloworld_pb2.py
とクライアント・サーバクラスを含むhelloworld_pb2_grpc.py
が再作成されました。
図で表すとこんなことをしています。
サーバとクライアントコードの修正
再作成によりコードが変わったのでgreeter_client.py
とgreeter_server.py
も修正が必要です。ここでは新しく増えたSayHelloAgain
を呼び出す修正を行なっています。
greeter_server.py
の更新
class Greeter(helloworld_pb2_grpc.GreeterServicer): def SayHello(self, request, context): return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) + def SayHelloAgain(self, request, context): + return helloworld_pb2.HelloReply(message='Hello again, %s!' % request.name)
greeter_client.py
の更新
def run(): # NOTE(gRPC Python Team): .close() is possible on a channel and should be # used in circumstances in which the with statement does not fit the needs # of the code. with grpc.insecure_channel('localhost:50051') as channel: stub = helloworld_pb2_grpc.GreeterStub(channel) response = stub.SayHello(helloworld_pb2.HelloRequest(name='you')) print("Greeter client received: " + response.message) + response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='you')) + print("Greeter client received: " + response.message)
gRPCアプリの起動
先ほどと同様に起動してみましょう。通信をするために仮想端末(Teratermなど)を2つ用意し、以下のコマンドで移動してください。
cd grpc/examples/python/helloworld
- Server側
python greeter_server.py
- Client側
python greeter_client.py
成功するとClient側にて以下のような表示になります。
client# python greeter_client.py Greeter client received: Hello, you! Greeter client received: Hello again, you!
ここではこのような通信が行われています。
gRPCを使ってできること
最初のgRPCのハンズオンでは以下のような動きをしていました。(最初はproto
からの生成は行なっていませんが)
このようにgRPCを用いるとprotoファイル
にて規定した内容でリクエスト・レスポンスが行えるようになります。
まだちょっとよくわからないなという方のためにこんなgRPCを用意してみます。
【ハンズオン】BMIを測定するgPRCを用意してみる
これを実装していきたいと思います。
まずはexamples/protos/helloworld.proto
を以下のように変更してください。リクエストとして身長と体重を受け入れて、レスポンスとしてBMIを返却するようにデータ構造を定義しています。
〜 // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} rpc SayHelloAgain (HelloRequest) returns (HelloReply) {} + rpc CalcBMI (BMIRequest) returns (BMIReply) {} } +message BMIRequest { + int32 height = 1; + int32 weight = 2; +} +message BMIReply { + float bmi = 1; +}
続いて先ほどと同様にprotoファイルからコードを生成します。
python -m grpc_tools.protoc -I../../protos --python_out=. --grpc_python_out=. ../../protos/helloworld.proto
そうしたらgreeter_server.py
とgreeter_client.py
を修正しましょう。
greeter_server.py
はこちら
def SayHelloAgain(self, request, context): return helloworld_pb2.HelloReply(message='Hello again, %s!' % request.name) + def CalcBMI(self, request, context): + height = float(request.height) / 100 + weight = float(request.weight) + bmi = weight / (height ** 2) + return helloworld_pb2.BMIReply(bmi = bmi)
greeter_client.py
はこちら。今回はpython greeter_client.py 160 60
でコマンドを実行すると「身長160、体重60」でBMIを計算するようにします。
def run(): # NOTE(gRPC Python Team): .close() is possible on a channel and should be # used in circumstances in which the with statement does not fit the needs # of the code. with grpc.insecure_channel('localhost:50051') as channel: stub = helloworld_pb2_grpc.GreeterStub(channel) response = stub.SayHello(helloworld_pb2.HelloRequest(name='you')) print("Greeter client received: " + response.message) response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='you')) print("Greeter client received: " + response.message) + import sys + height = int(sys.argv[1]) + weight = int(sys.argv[2]) + response = stub.CalcBMI(helloworld_pb2.BMIRequest(height=height, weight=weight)) + print("BMI: " + str(response.bmi)) 〜
ここまでできたら
先ほどと同様に起動してみましょう。通信をするために仮想端末(Teratermなど)を2つ用意し、以下のコマンドで移動してください。
cd grpc/examples/python/helloworld
- Server側
python greeter_server.py
- Client側
python greeter_client.py
成功するとClient側にて以下のような表示になります。
client# python greeter_client.py 160 60 Greeter client received: Hello, you! Greeter client received: Hello again, you! BMI: 23.4375 client# client# python greeter_client.py 180 60 Greeter client received: Hello, you! Greeter client received: Hello again, you! BMI: 18.5185184479
実際にこれが実装できましたね。イメージつきましたでしょうか。
まとめ
このようにproto
ファイルを作成することで「何をパラメータとして渡せば良いのか、その結果何が帰ってくるのか」を示すことができるのがgRPCの特徴です。
REST APIではAPIを作成する行為と「何をパラメータとして渡せば良いのか、その結果何が帰ってくるのか」というドキュメントを作成する行為は別だったためAPIの更新に手間取ることがありました。
gRPCを利用する場合は必ずprotoファイルを先に作る必要がありますので、protoファイルがドキュメントとなりそのような問題は発生しません。
また、今回はpythonで試しましたが同じprotoファイルから様々な言語向けのコードを生成することができるため、言語を超えて通信を簡単にすることができるようになります。