Rails アプリケーションから gRPC サーバー に接続するまで
Rails アプリケーションから gRPC サーバーに接続するまで
そもそも RPC とは
あるコンピュータで動作するソフトウェアから、通信回線やコンピュータネットワークを通じて別のコンピュータ上で動作するソフトウェアへ処理を依頼したり、結果を返したりするための規約。
要するに別アプリにある処理(関数)を呼び出すこと。
API はこれに当たるのかなと思っています。
gRPC とは
gRPCはRPCを実現するためにGoogleが開発したプロトコルの1つで、インターフェイス定義言語のもとになるメッセージ交換形式として Protocol Buffers を利用できます。gRPC上のアプリケーションでは、別マシン上にあるアプリケーションのメソッドをローカルオブジェクトのように直接呼び出すことができ、分散アプリケーションおよびサービスの作成を簡単にできます。
Protocol Buffers が現時点ではわかりませんが、RPC を実現するためのプロトコルを Google が定めたものという認識でいます。
Protocol Buffers
インターフェース定義言語(IDL)でデータ構造を定義する通信や永続化での利用を目的としたシリアライズフォーマットであり、Googleにより開発されています。
Protoc インストール
protoc は protocol buffers で定義されるスキーマから構造体やクラスを生成するコンパイラ。
Mac では homebrew でインストールが可能みたいだけどなぜか出来なかったのでソースコード落としてきてビルドしました。
解凍後、make コマンド実行!
Go で gRPC サーバーを実装
まずは実装例が多い Go 言語で書いてみたいと思います。
Go をあまり書いたことがないため少々時間がかかりましたが、下記の記事を参考に作りました。
記事中ではリクエストで受けた文字列をそのまま返す GetEcho という関数を実装しています。
$ go run client.go result:&echoService.EchoResponse{Input:"hiracky16", XXX_NoUnkeyedLiteral:struct {}{}, XXX_unrecognized:[]uint8(nil), XXX_sizecache:0} error::<nil>
成功したっぽい。
Rails から gRPC サーバーに問い合わせる
ここからが本題です。 今度は Rails サーバーから GetEcho を呼び出します。
まず Gemfile に 2 つの gem を追加します。
grpc は Ruby で grpc を実現されるための gem、
grpc-tool は参考記事で作成した Protocol Buffers を Ruby のクラスに変換するツールです。
gem 'grpc' gem 'grpc-tools'
次に以下のコマンドを実行し、
$ bundle exec grpc_tools_ruby_protoc -I ../service --ruby_out=lib --grpc_out=lib ../service/*.proto
これによって Rails プロジェクトの lib 配下に echoService_pb.rb
と echoService_services_pb.rb
が生成されます。
(proto ファイルがキャメルケースだったためファイル名までそうなってしまった。。)
簡単に説明すると echoService_pb.rb
では文字列やシンボルベースで proto ファイルの内容からリクエストとレスポンスのクラス(GetEchoMessage, EchoResponse)を作っていた。
要するに Ruby で proto ファイルの内容を表現したものです。
それらのクラスを使って echoService_service_pb.rb では Echo という module の形で get_echo というメソッドを提供しています。
echoService_service_pb.rb の内容は以下です。
# Generated by the protocol buffer compiler. DO NOT EDIT! # Source: echoService.proto for package '' require 'grpc' require 'echoService_pb' module Echo class Service include GRPC::GenericService self.marshal_class_method = :encode self.unmarshal_class_method = :decode self.service_name = 'Echo' rpc :GetEcho, GetEchoMessage, EchoResponse end Stub = Service.rpc_stub_class end
これをコントローラーから呼び出します。
require 'echoService_services_pb.rb' require 'echoService_pb.rb' class ApplicationController < ActionController::API def echo echo_stub = Echo::Stub.new('localhost:2525', :this_channel_is_insecure) res = echo_stub.get_echo(Echo::Service::GetEchoMessage.new(target_echo: 'test')) render json: { message: res.input } end end
- echoService_service_pb.rb に記述のあった stub のインスタンスを生成
- 引数には GetEchoMessage のインスタンスを new して上げて、get_echo(スネークケースなので注意)を呼ぶ
- レスポンスに EchoResponse のインスタンスが返ってくるので proto ファイルで指定のあった input というキーの値を取り出して返す
まとめ
やっと gRPC について勉強することができました。 Protocol Buffers を使ってスキーマを定義しておくといろいろなサービスや言語で使い回すことができるので、マイクロサービス設計には欠かせないのかもと思いました。
また、たとえ proto ファイルの内容が変更された場合でも生成し直せば動くことが期待されるため、スキーマの変更に強いのも使われている要因なのかなと思います。
プライベートで gRPC を使うことはそうそうないかもですが、チーム開発では便利なので使える場面があったら試してみたいです。