この記事では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ファイルから様々な言語向けのコードを生成することができるため、言語を超えて通信を簡単にすることができるようになります。
