ProtocolBuffers
gRPC
OrigamiDay 2

gRPCを導入してみて感じたよかったことと遭遇した問題

導入

2017年、OrigamiでもgRPCの導入が進みました。
もともと、2年ぐらい前からIDLによる定義とそれを利用したRPCの利用についてはときどき話されていましたが、既存機能をgRPCに置き換えるモチベーションや積極的な理由もあまりなく、HTTPによる通信にものすごく困っていたわけでも無いため、なかなかスタートが切り出せずにいました。
しかし完全新機能の一部をgRPCで作ったところ、それに携わったメンバーからの感触もよく、gRPCの利用範囲を広げて行くことにしました。
OrigamiにおけるgRPC利用はサーバー間通信から始めており、クライアントアプリから直接gRPCアクセスすることは(今の所)行われていません。
またサーバサイドの開発言語もほとんどがGoで書かれており、言語間のインピーダンスミスマッチについての評価はできていません。

遭遇した問題

コミュニケーションコスト、ドキュメンテーションコストが減る

最大の利点はコミュニケーションコスト、ドキュメンテーションコストが減ることだと思います。
Protocol Bufferによる定義があるので、値の型定義についてはとりあえず定義ファイルを見ることですぐにわかります。また生成されたソースコードがあるので、ソース補完が効くのもGo使いにとっては非常にありがたいです。
定数の一部についてもenumで定義できるので、公開された定数についてもあるていどドキュメンテーションされることになります。
ただし値やフィールドの意味や、受け取る値の範囲などは定義できないので適宜コメントやドキュメントに書いて行く必要はあります。proto3からはrequiredやoptionalというものも無くなったので、このあたりもコメントするポイントです。

テストしやすくなる

HTTP通信であってもGo標準パッケージにある net/http/httptest を使うことでテストできていたのですが、gRPCの場合はRPCが見かけ上単なる関数呼び出しになるため、テストが非常にやりやすくなりました。
またパラメータのバインディング処理が無くなったため、コードもすっきりするようになりました。
コーディング規約として、RPCの引数と返り値にScalar Value Typesを用いず、Message Typesを使うことと決めています。

テスト用CLIアプリが作りやすい

Goの得意領域としてCLIアプリが作りやすいということが良く言われますが、この特性はgRPCと非常に相性が良く、接続元サーバーが出来上がる前からCLIツールを作ることで結合テストが簡単に行えるようになります。
またこのCLIツールでテストデータの作成もSQLでDBに直接突っ込むのでは無く、正しい手順で行えるため、意図せぬ不正データが発生することを防止できます。

よくなかったこと

定義ファイル更新のコンフリクトが起きる可能性がある

Protocol Bufferでの構造体(message)定義では、フィールドの順序を決める必要がありますが、開発タイミングによってはこの順序がコンフリクトする可能性があります。

例えば以下のような検索サービスに対するリクエストメッセージの定義があったとします。
proto3
message SearchRequest {
string query = 1;
}

ここで検索結果の数を制御するcountというフィールドを追加する要件が生まれたとすると、定義は以下のようになります。

message SearchRequest {
  string query = 1;
  int32 count = 2;
} 

一方で結果の並び順を制御するorderというフィールドを追加する要件が発生し、その要件を受け取った人はcountの要件を知らないと以下のような定義を書いてしまうと、フィールドの番号が被ってしまいます。

message SearchRequest {
  string query = 1;
  string order = 2;
} 

これくらいであれば後から話し合って調整することも可能ですが、より複雑なケースでは非常に面倒なことになります(同名のフィールドに別の意味を与えるとか)。

この問題については全体に目を配って早め早めに調整するしか手はないかなと思っています。

うっかり古い定義が混ざってもすぐに気づけない

新旧の定義が混ざった時にすぐにエラーとなってくれればよいのですが、なんとなく動いてしまうケースも多く、原因にたどり着くのに時間がかかることがありました。
ただこれはgRPC固有の問題ではなくHTTPでも起こり得ます。

バイナリ通信であるがゆえに起きる諸々

gRPCはバイナリ通信なので、通信経路の途中でログを出すといったことが出来ないというのは地味に辛いです。
それと気軽にcurlで叩いて動かすということが出来ないため、疎通確認が面倒だったりします。
接続確認のためのPING-RPCを定義して、疎通用としていますが、それでもcurlに比べると面倒です。
そのほか普通のHTTPと比べるとやはり周辺ツールの不足というものは感じます。

gRPCは使う価値があるか

特有の問題点はあるもののJSONやFORMと比べた時、やはりProtocol Buffersによる定義があることは非常に有利に思います。
運用を続けて行くうちにトラブルに遭遇することはあるでしょうが、サーバー間通信についてはgRPCを利用する価値はあると考えています。