はじめに
今回は機会があって、gRPCをPythonでやったので投稿してみることにした。
gRPCとは?
gRPCとは、Googleによって開発されたオープンソースのRPC(Remote Procedure Call)フレームワームのこと。
公式ページをご覧になりたい方はこちらを参照してください。
といっても、RPCという単語にあまり聞きなじみがない方も多いかもしれないので、ちょっとRPCについて、触れていく。
def say_hello():
print("Hello World!")
def main():
say_hello()
if __name__ == "__main__":
main()
2つのメソッドがある。
これは、このソースコードを実行した環境で呼び出しから実行まで完結していることがわかる。
つまり、これは以下のように言い換えることができる
mainメソッドから、say_helloというProcedure(関数、メソッド)をCall(呼び出し)している
では、このsay_helloメソッドが実行環境とは別のPC、もしくはサーバー上にあった場合どうだろうか?
状況としてはこうなる
このように、ローカルとは別のサーバー上の関数(メソッド)を呼び出すことを、
リモート(Remote)環境の関数(Procedure)を呼び出す(Call)ことから、Remote Procedure Call(RPC)と呼ばれる。
それでは、本題。
まず、gRPCの特徴。
特徴
-
通信規格は、HTTP/2であること
-
protobufと呼ばれる、シリアライズ方式を利用すること
この二つが大きな特徴ある。
HTTP/2
HTTP/2 を利用することで、以下のような利点がある。
-
HTTP/2の特徴であるリクエストとレスポンスの多重化が利用でき、その結果短時間に大量データのやり取りをすることができる
-
高速な双方向のストリーミングが可能
protobuf
-
データをやり取りするためのスキーマ定義をするフォーマット
-
この規格を利用することでAPI仕様を統一することができる
基本的な流れ
1.protoファイルを作る
2.proto-toolでコード生成する
3.自動生成されたソースコードを利用して、サービスの実装
4.main関数(エントリポイント)でサービスをgRPCサーバーに登録する
図にすると、このような流れ。
事前準備
PythonでgRPCを扱う場合、以下の2つのライブラリが必要。
-
grpcio
-
grpcio-tools
以下のコマンドでインストールは可能。
pip install grpcio
pip install grpcio-tools
1.protoファイルを作る
今回はこのようなprotoファイルにしてみた。
syntax = "proto3";
package hello;
service SayHello {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
2.proto-toolでコード生成する
protoファイルができたので、このあと使うソースコードの生成をしてく。
python -m grpc_tools.protoc -I../proto/ --pyi_out=./ --python_out=./ --grpc_python_out=./ ./hello.proto
このコマンドを実行するか、もしくは以下のようなPythonのコードを実行することでも生成できる。
from grpc.tools import protoc
protoc.main(
(
'',
'-I../proto/',
'--pyi_out=./',
'--python_out=./',
'--grpc_python_out=./',
'hello.proto'
)
)
すると、このようなソースファイルが生成されるのて準備完了!
-
hello_pb2_grpc.py
-
hello_pb2.py
-
hello_pb2.pyi
3.自動生成されたソースコードを利用して、サーバーの実装
サーバー側の実装をしていく。
サーバー側にはサービスを提供するためのサービサーが必要なので、その用意していく。
import hello_pb2
import hello_pb2_grpc
class SayHelloServicer(hello_pb2_grpc.SayHelloServicer):
def SayHello(self, request, context):
return hello_pb2.HelloResponse(message="Hello, %s!" % request.name)
4.main関数(エントリポイント)でサービスをgRPCサーバーに登録する
ここまで準備したサービスを利用できるようにサーバーにサービスを登録していく。
from concurrent import futures
from hello_pb2_grpc import add_SayHelloServicer_to_server
from hello_servicer import SayHelloServicer
import grpc
def serve():
port = "50051"
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
add_SayHelloServicer_to_server(SayHelloServicer(), server)
server.add_insecure_port('localhost:' + port)
server.start()
print('server started..., listening on ' + port)
server.wait_for_termination()
if __name__ == "__main__":
serve()
ここでは、サーバーは50051番ポートを利用。
次に、このサーバーを利用するために、クライアントを用意すると完成。
クライアント側のソースコードはこれ。
from __future__ import print_function
import grpc
import hello_pb2
import hello_pb2_grpc
def run():
print("client start...")
with grpc.insecure_channel("localhost:50051") as channel:
stub = hello_pb2_grpc.SayHelloStub(channel)
response = stub.SayHello(hello_pb2.HelloRequest(name="Alice"))
print("received: " + response.message)
if __name__ == "__main__":
run()
5.それぞれ起動してみよう
サーバー、クライアントの順で実行していく。
すると、こうなって
サーバー:
python3 main.py
server started..., listening on 50051
クライアント:
python3 main.py
client start...
received: Hello, Alice!
クライアントからサーバーへのリクエスト、サーバーからクライアントへのレスポンスの両方が確認できたので、おわり!