理系学生日記

おまえはいつまで学生気分なのか

忍者TOOLS

gRPC の Interceptor 概観

Unary と Streaming

Interceptor の前に gRPC の種類について書かないといけないのですが、 gRPC には 大きくわけて 4 つの RPC が存在しています。

  1. Unary RPC
  2. Server streaming RPC
  3. Client streaming RPC
  4. Bidirectional Streaming RPC

一番分かりやすいのは Unary RPC で、これは一般の RESTful API 等と同様に、クライアントがリクエストを送出するとサーバがそれに対するレスポンスを返却するというものです。 イメージも分かりやすい。

次に分かりやすいのは Server streaming RPC で、これはクライアントの 1 リクエストに対して、サーバは stream に載せて何回でもレスポンスを返却できるというモデルです。 一度クライアントからリクエストを送っておけばサーバが chunked 通信みたいなこともできるでしょう。また、必要なタイミングでサーバがクライアントにメッセージを送信することができるという意味で、サーバ -> クライアントへの PUSH 型通信も実現できます。

Client streaming RPC は、今度はクライアントが stream に載せてリクエストをサーバに送信できるというモデルです。巨大な画像ファイルを gRPC でアップロードしたいとか、そういう用途なんだろうと思います。

Bidirectional Streaming RPC は、クライアント・サーバの双方が stream でやりとりをするタイプで、チャットみたいな通信が実現できます。 以下の動画では、Chat が bidirectional streaming の形で proto に定義されているのが確認できます。


gRPC 101 for Java Developers - WEB2DAY 2016

4 つあると言いましたが、これを 2 つに分けるとすると、Unary、Streaming の二択です。

Interceptor

gRPC には Interceptor という概念が存在します。 サーバ、クライアントそれぞれに対して、Unary 用と Streaming 用の 2 つが存在するため、2 x 2 = 4 種類の Interceptor があります。

サーバサイドの Interceptor に関しては、まぁ Java でいう Servlet Filter みたいなもので、gRPC のメソッドハンドラ呼び出し前後で任意の処理を挟み込めます。 クライアントサイドの Interceptor についても RESTEasy とかの Interceptor と同様に、gRPC の呼び出し前後で任意の処理を行えるということができます。

というわけで、gRPC の処理のうち、認証やロギング、データベーストランザクション管理といった共通的なものについては、ガンガン Interceptor に移譲させるべしだと思っています。

mercari

(ぼくは Mercari のエンジニアではありませんが)たとえば Mercari では、以下のリポジトリで interceptor を管理しているっぽくて、

  • サーバサイドで panic が発生すると grpc の internal エラーに変換して返却する panichandler
  • zap ロガーをメソッドハンドラが使えるように context にぶちこむ zap
  • jsonpb を使って送受信ログを実現する requestdump
  • 定義した個々の interceptor をチェーンとして登録できる multiinterceptor

などなどが公開されています。

go-grpc-middleware

Mercari という企業ベースの Interceptor ではありますが、もちろんもう少し企業色のない Interceptor 群も公開されていまして、ぼくは専らこちらを使ってます。

Mercari と同じような機能を持つものも、まったく違う機能もありまして、たとえば

  • panic を(デフォルトでは) internal に変換する recovery
  • 構造化ログを出力できる logrus のロガーを context にぶちこんでメソッドハンドラで使えるようにする logrus
    • 送受信ログを (jsonpb を使って) 実現する Payload*Interceptor もここに含まれています
  • リクエスト、レスポンスの情報を tag としてコンテキストに叩き込む tags
  • .proto に記載した制約を使って単項目バリデーションを実現する validator
  • 個々の Interceptor を chain として登録できる interceptor (go-grpc-middlware 直下で公開されています)

といったものがあります。

実は個々の Interceptor によってかなり開発のアクティブ度が異なっていたりするので、お勧めできるもの・できないものがあります。 ただ、あんまり日本語での情報もない部分なので、今後こういうところを掘り下げたエントリを書ければ良いなと思っています。