こんにちは!
今回はじめてブログを書くことになった、山崎です。
1年が始まったと思ったら、もう1年が終わりますね、、、
みなさんはどんな1年でしたか?
さて、今回は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メソッドを呼ぶだけのpythonのソースコードです。
これは、このソースコードを実行した環境で呼び出しから実行まで完結しています。
つまり、これは以下のように言い換えることができますね。
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
hello_pb2.pyの中を見ていただくと、このファイルでメッセージをシリアライズしたり、デシリアライズしていることがわかります。
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!
クライアントからサーバーへのリクエスト、サーバーからクライアントへのレスポンスの両方が確認できましたね!
おわりに
ここまで読んでいただきありがとうございました。
gRPCはGoogleやいろいろな企業のバックエンドで利用されている技術でマイクロサービスの領域では注目されているものです。
Open APIのSwaggerのように、protoファイルがそのままスキーマ定義になるのはなかなかに魅力的かと思います。
コード生成ツールも様々な言語に対応していますので、その点も安心ですね。
お知らせ
ecbeingでは新しい技術要素に触れていきたいエンジニアを募集しています!! careers.ecbeing.tech