chroju.dev/blog

the world as code

gRPC ちょっと理解した

gRPC に入門した。favicon のマスコット?めっちゃかわいいですね。。

きっかけは 3rd Party tool をきっかけに Terraform のソースコードを少し嗜んだ話 · the world as code という記事で触れたように、 Terraform 0.12 で Terraform Core と Provider が gRPC で通信するようになったため。知ってなてくは Terraform を使えないわけでも Provider を書けないわけでもないのだが、昨今よく聴く単語だし、 microservices などにも必要な要素技術なので入門してみた。

教材

ちょうど WEB+DB PRESS の最新号で特集されていたので、主にこれを使った。

WEB+DB PRESS Vol.110
WEB+DB PRESS Vol.110
posted with amazlet at 19.06.21
藤村 大介 森田 リーナ 渡邉 祐一 市原 創 板倉 広明 高橋 征義 笹田 耕一 大原 壯太 新倉 涼太 末永 恭正 久保田 祐史 牧 大輔 東 邦之 星 北斗 池田 拓司 竹馬 光太郎 はまちや2 竹原 八谷 賢
技術評論社
売り上げランキング: 13,606

あとは公式のドキュメント。英語で簡単な Tutorial が書かれているほか、 gRPC のレポジトリ内にある examples というフォルダに言語別の実装例が書かれていて参考になった。

学んだこと

写経

gRPC とは何か、などとここで改めてまとめても仕方ない感があるので、それについては割愛する。 WEB+DB PRESS を読もう。

当該号の特集では、 gRPC の4種類の通信方式(Unary, Server streaming, Client streaming, Bidirectional streaming)それぞれの簡単な実装サンプルと、実践例として gRPC を用いたタスク管理サービスの作り方が掲載されており、これを適宜写経しながら進めた。コードはいずれも GitHub で公開されている。

GitHub - vvatanabe/go-grpc-basics: WEB+DB PRESS Vol.110 特集2「[速習]gRPC」サンプルコード
WEB+DB PRESS Vol.110 特集2「[速習]gRPC」サンプルコード. Contribute to vvatanabe/go-grpc-basics development by creating an account on GitHub.
GitHub - vvatanabe/go-grpc-basics: WEB+DB PRESS Vol.110 特集2「[速習]gRPC」サンプルコード favicon https://github.com/vvatanabe/go-grpc-basics
GitHub - vvatanabe/go-grpc-basics: WEB+DB PRESS Vol.110 特集2「[速習]gRPC」サンプルコード
GitHub - vvatanabe/go-grpc-microservices: WEB+DB PRESS Vol.110 特集2「[速習]gRPC」
WEB+DB PRESS Vol.110 特集2「[速習]gRPC」. Contribute to vvatanabe/go-grpc-microservices development by creating an account on GitHub.
GitHub - vvatanabe/go-grpc-microservices: WEB+DB PRESS Vol.110 特集2「[速習]gRPC」 favicon https://github.com/vvatanabe/go-grpc-microservices
GitHub - vvatanabe/go-grpc-microservices: WEB+DB PRESS Vol.110 特集2「[速習]gRPC」

Ruby での再実装

単に写すだけというのもつまらないので、ちょっとした応用もやってみた。 gRPC には遣り取りするデータのシリアライズフォーマットを定義した Protocol Buffers (protobuf) を元として、サーバ / クライアントの実装を様々な言語で生成することができるという特徴がある。そこで、誌面のサンプルはサーバ / クライアントともに Go で書かれていたが、クライアント側を Ruby で書いてみることにした。

対象にしたのは Server streaming gRPC のサンプルコード。主に公式の Ruby 向け Tutorial を見つつ、先の examples 内の実際のコードも見ながら進めたが、以下の記事も参考にした。

RubyでgRPCのストリーミング - Qiita
概要gRPCの双方向なストリーミング機能をRubyで利用する方法前提以下でgRPC用のgemをインストール gem install grpc-tool…
RubyでgRPCのストリーミング - Qiita favicon https://qiita.com/yururit/items/bc7c0eda63d5fa30289a
RubyでgRPCのストリーミング - Qiita

まず、必要な gRPC 関連の gem をインストール。

$ gem install grpc
$ gem install grpc-tools

続いて protobuf から Ruby 用のクライアントコードを生成しようと思ったのだが、サンプルコードでは protobuf の package が file という名前で定義されており、これをそのまま Ruby のコードに変換すると、 File class と衝突する形になってしまった。そのため package 名を filedl という名前に変更してからコード生成を実行している。ネーミングセンスは気にしないことにした。なお、このコマンドは元のサンプルコードの downloader というフォルダ内に ruby フォルダを掘り、その中で実行している。

$ mkdir lib
$ grpc_tools_ruby_protoc -I ../proto --ruby_out=lib --grpc_out=lib ../proto/filedl.proto

Ruby のコードは以下のようになった。元々の Go によるサンプルコード(go-grpc-basics/main.go at master · vvatanabe/go-grpc-basics)とだいたい同じ動きをするようにしている。

this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(this_dir, 'lib')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

require 'grpc'
require 'filedl_services_pb'

def main
    stub = Filedl::FileService::Stub.new('localhost:50051', :this_channel_is_insecure)
    filename = ARGV[0]
    resps = stub.download(Filedl::FileRequest.new(name: filename))
    blob = ""
    resps.each do |r|
        blob << r.data
    end

    p "done " + blob.size.to_s(10) + " bytes"

    file = File.open(filename,"w")
    file.puts blob
    file.close
end

main

これで ruby client.rb test などと実行することで無事に動作が確認できた。異なる言語間でシリアライズされたデータの遣り取りがだいぶ簡単に実装できてなるほどねぇという感じはした。あと Ruby を3年ぶりぐらいに書いたのでところどころ文法にビビったりした。 << 演算子とか。

成果物はすべて chroju/learning_grpc に上げておいた。

Terraform と gRPC

一通りドキュメントを見てはみたが、正直そんなにガッツリ gRPC 使ってどうこうみたいなことは書いていないような気がする。 RPC 使ってますよ(これは Terraform 0.11 以前から同様)という話と、 "Although technically possible to write a plugin in another language, almost all Terraform plugins are written in Go." という一文が Writing Custom Providers - Guides - Terraform by HashiCorp にあるぐらい。将来的に Go 以外でも書けるようにするんですかね。どうなんですかね。

コードで言えば terraform/docs/plugin-protocol · hashicorp/terraform に protobuf の定義ファイルがある。また、 Provider は terraform/pluginServe という関数を main.go に書く必要があり、この関数が Terraform Core に Provider を渡しているようなのだが、 この実装 を見ると、現時点では gRPC 向けに既存のメソッドを変換したりする処理が入っているみたい。 Provider を書く際には、 gRPC を意識する必要は基本的にはなさそう。