1.はじめに
Elixir~Python間を、gRPCを使って異言語間通信を試してみます。
今回の記事では、
- gRPCサーバ:Elixir
- gRPCクライアント:Python3
としています。
実行環境
| ハード | Raspberry Pi 3B+ |
| OS | Raspbian Buster, Ubuntu Server 20.04LTS |
| Python | 3.7.3 |
| Elixir | 1.7.4 (compiled with Erlang/OTP 21) |
| ※いづれもaptパッケージでインストールできるもの |
2.Elixir側
最初に、Elixir側の準備をします。
ここでは、elixir-grpcのライブラリに含まれているサンプル「examples/helloworld」を使ってみます。
クライアントからnameに名前を代入してリクエストすると、サーバからは"Hello "という文字列が帰ってきます。
※詳しい手順は別記事にまとめてますので、こちらをご覧下さい。
$ pwd
..../elixir/
# クローンします
$ git clone https://github.com/elixir-grpc/grpc.git
# サンプルディレクトリに移動して
$ cd ./grpc/examples/helloworld/
# 依存関係の処理、コンパイルします。
$ mix do deps.get, compile
2.Python側
次はPython側でクライアント・サーバを作る手順を示します。
(1)grpcツールのインストール
$ pip3 install grpcio grpcio-tools protobuf
(2)インターフェースの生成
protoファイルを解析して、Python向けのインターフェースを生成するツールを準備します。
$ pwd
..../elixir/grpc/examples/helloworld
# Pythonのスクリプトを保存するフォルダを作成
$ mkdir python
$ cd python
# elixirのほうで定義している、protoファイルをコピー
python $ cp ../priv/protos/helloworld.proto ./
# ツールのファイルを生成
python $ touch codegen.py
python $ chmod 755 codegen.py
Python向けのインターフェースを生成するツールのソースコードです。
今回はhelloworld.protoを対象にしてるので、コード中では__NAME="helloworld"としています。
# !/usr/bin/env /usr/bin/python3
# -*- coding: utf-8 -*-
"""
protoファイルのコンパイル
以下の2つのファイルが生成されます。
・*_pb2.py : シリアライズのインターフェース
・*_pb2_grpc.py : gRPCのインターフェース
"""
from grpc.tools import protoc
# protoのファイル名(の名前部分)
__NAME="helloworld"
# Python向けのインターフェースを生成
protoc.main(
(
'',
'-I.',
'--python_out=.',
'--grpc_python_out=.',
('{}.proto'.format(__NAME)),
)
)
Python向けのインターフェースを生成します。
python $ ./codegen.py
python $ ls
codegen.py helloworld_pb2_grpc.py helloworld_pb2.py helloworld.proto
*_pb2.pyと、*_pb2_grpc.pyの二つのファイルが生成されました。
(3)Python側クライアントの作成
クライアントのスクリプトを作成
# ファイルを生成
python $ touch client.py
python $ chmod 755 client.py
# !/usr/bin/env /usr/bin/python3
# -*- coding: utf-8 -*-
"""
gRPCクライアント
by myasu 2020
"""
import sys
import grpc
# 先ほどprotoから生成したインターフェースをインポート
import helloworld_pb2
import helloworld_pb2_grpc
def grpc_client(request):
"""gRPCクライアントの通信処理
Parameters
----------
request : string
gRPCサーバに送るメッセージ
"""
# Elixir側のgRPCサーバのアドレスとポートを指定して接続
with grpc.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
# Elixir側のgRPCサーバにrequestする
response = stub.SayHello(helloworld_pb2.HelloRequest(name=request))
# responseの内容を確認
print(' Response:', response.message)
if __name__ == '__main__':
"""メイン処理
"""
# 引数の読み込み
args = sys.argv
# 引数の長さをチェック
if len(args) == 2:
# 実行
grpc_client(args[1])
else:
# エラー
print('Arguments are too short')
Python→Elixir→Python間の通信テスト
最初にサーバ・Elixir側を起動します。
$ mix grpc.server
10:58:01.872 [warn] cowlib should be >= 2.9.0, it's 2.8.1 now. See grpc's README for details
10:58:01.977 [info] Running Helloworld.Endpoint with Cowboy using http://0.0.0.0:50051
(・・・ここから先はクライアントの要求があったときに表示・・・)
22:58:04.739 [info] Handled by Helloworld.Greeter.Server.say_hello
22:58:04.744 [info] Response :ok in 4ms
22:58:09.237 [info] Handled by Helloworld.Greeter.Server.say_hello
22:58:09.237 [info] Response :ok in 15μs
22:58:13.552 [info] Handled by Helloworld.Greeter.Server.say_hello
22:58:13.552 [info] Response :ok in 15μs
[Ctrl-\]で停止
次にクライアント・Python側を実行します。
スクリプトの引数には、任意のメッセージが指定できます。
python $ ./client.py chika
Response: Hello chika
python $ ./client.py you
Response: Hello you
python $ ./client.py ruby
Response: Hello ruby
python $ ./client.py CYaRon!
Response: Hello CYaRon!
メッセージの先頭に、サーバ側が"Hello "を付けて返してきます。
このような感じで、異種言語間の通信が出来ました。
4.その他
(1)grpc-toolsのインストール方法
20年6月現在、RaspbianやUbuntu 20.04LTSのaptでpython3-grpcio python3-grpcio-toolsでインストールすると、下記バージョンが入ります。
| python3-grpc-tools | 1.14.1-1build3 | Protobuf code generator for gRPC (Python 3) |
| python3-grpcio | 1.16.1-1ubuntu5 | GRPC system (Python 3) |
1.23.0よりも前のバージョンでは、wait_for_termination命令がサポートされていませんので、実行時に下記のエラーメッセージが出ます。
AttributeError: '_Server' object has no attribute 'wait_for_termination'
従って、現時点ではaptではなく、pip3でインストールして下さい。
参考: https://github.com/grpc/grpc/issues/20333