gRPC のクライアントを Ruby で実装する手順を備忘も兼ねて書いておく
まずは gem を入れる
- Ruby で gRPC を利用するために
grpc
gem をインストール- これを入れることで、クライアントの実装に必要な class 群を利用できるようになる
gem 'grpc', '~> X.XX.X'
.proto ファイルの取得とコンパイル
- .protoファイルの管理方法はいくつかあるが、詳細はこちらの記事を参照
- .proto を rb ファイルにコンパイルするには
grpc_tools
という Gem を利用する
$ gem install grpc-tools
- コンパイルのコマンドは以下のような感じ
$ grpc_tools_ruby_protoc -I ./proto \--ruby_out=lib --grpc_out=lib ./proto/hoge_proto/*.proto
-
-I
オプションで .proto ファイルを探索するディレクトリを指定 -
--ruby_out
オプションで、コンパイル後の出力先ディレクトリを指定- どこでもいいんだけど /lib が多いらしい
-
--grpc_out
オプションの第一引数にはコンパイル後の stub となる class ファイルの出力先を、第二引数にはコンパイル対象の .proto ファイルのあるディレクトリを指定 - コンパイルが成功すると、各 proto に対応する pb.rb ファイルと、それらを全てインポートしてmodule化した hoge_services_pb.rb ファイルが生成される
- この hoge_services_pb.rb に rpc メソッドの定義と Stub の定義があり、呼び出しにはこの class を利用していく形になる
クライアントスタブの実装
上記で準備は整ったので、ここから実際に実装していく。
まずは gRPC コールを行うために必要なスタブインスタンスを作成する。
スタブインスタンスの作成に関しては、/initializer 配下にファイルを作って、そこで new するというやり方もよく見るけど、グローバル変数に突っ込むのも嫌だったので、今回は model クラスを切ってシングルトンパターンを適用する方向で実装。
正直この辺のベストプラクティスはまだ模索中。
model/grpc/hoge_service_client.rb
require 'proto/hoge_services_pb'
require 'singleton'
# ここは環境ごとに設定ファイルから取ってくる作りにした方が良い
END_POINT = "localhost:50051"
class Grpc::HogeServiceClient
include Singleton
attr_reader :stub
def initialize
@stub = HogeProto::HogeService::Stub.new(END_POINT, :this_channel_is_insecure)
end
end
- Stubインスタンス作成の部分は、上記にも書いたように hoge_services_pb.rb に記載がある Stub を new してインスタンスを作成している
- new の第一引数は gRPC サーバーのエンドポイントを指定
- 第二引数は、rpc 通信時の暗号化用の引数で、ちゃんと運用する場合はSSL証明書のパスなどを指定することになる
実際に gRPC サーバーをコールする
上記で作成した Stub を使って実際に rpcコールしてみる
grpc/hoge.rb
class Grpc::Hoge
def get_name(params)
# Stub インスタンスを腹持ちするmodelのインスタンスを作成
client = Grpc::HogeServiceClient.instance
# rpc コールに必要なリクエストデータを作成
req = get_name_request(params)
# 必要があればメタデータも作成
meta = Hash.new
meta["x-app-id"] = "hogeghoge"
meta["x-app-password"] = "fugafuga"
begin
# ここで rpc コール
client.stub.get_name(req, {metadata: meta})
res = "success"
rescue GRPC::BadStatus => ex
res = "error"
rescue ex
res = "unexpected error"
end
res
end
private
def get_name_request(params)
HogeProto::GetNameRequest.new(
id: params[:id]
)
end
-
client.stub.get_name
の部分で rpc コールしている- この呼び出しの結果がエラー(Status.Codes が OK ではない)の場合、exceptionが発生するので begin で囲んで エラーの場合は rescue するようにする
-
meta
に関しては、gRPC コール時のメタデータのセットをしている- ここではIDとパスワードをセットしているが、共通で必要なものは stub を作る model 側でインスタンス変数としてイニシャライズした方が良さそう
- これは普通に Hash 形式で値をセットすれば良いらしい
- ただし、キーには大文字が使えないっぽいので注意
エラーハンドリングについて
先ほども書いたが、rpc呼び出しでエラーステータスのレスポンスが返ってきた場合、GRPC::BadStatus
class の例外が発生する。
なので、必ず begin~rescue する必要がある。
また、サーバー側が error_details を詰めて返してきた場合は、それを取り出すための実装も必要になる。
参考:https://www.rubydoc.info/gems/grpc/GRPC/BadStatus