概要
gRPCのInterceptorのClient側、Server側の基本的な使い方を紹介します。
環境
- golang 1.12.0
- grpc 1.18.0
Interceptorの種類
以下の4つがあります。今回はUnaryの方の使い方を説明します。
サーバサイド
クライアントサイド
UnaryServerInterceptor
まずはよくあるサーバ側の設定です。
type UnaryServerInterceptor func( ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler ) (resp interface{}, err error)
と定義されているので、この通りの関数を用意します。
それぞれ説明すると
変数 | 意味 |
---|---|
ctx | context。クライアントでセットされたmetadataとか入ってる |
req | クライアントからのrequest body |
info | server info。メソッド名とか分かる |
handler | クライアントから呼ばれたgRPCメソッド |
resp | response body。handlerを実行すると吐き出される |
具体的な実装
サーバにきたリクエストのログ、レスポンスのログを吐き出すようなinterceptorを用意します。
func LogBodyInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { // 1. request bodyをprint fmt.Printf("%+v\n", req) // 2. リクエストされたgrpcメソッドを実行 resp, err := handler(ctx, req) if err != nil { return nil, err } // 3. response bodyをprint fmt.Printf("%+v\n", resp) return res, nil } }
ポイント
- handlerの前に書けばgrpcメソッドを実行する前に処理される
- handlerの後に書けばgrpcメソッドを実行した後に処理される
今回は分かりやすくこのように書いていますが、実際は3. response bodyをprint
はエラー時もログを吐きたいと思うのでdefer
で実行するのが普通ですね。
grpcサーバにセットする方法
こんな感じにNewServer
する時の引数に与えます。
s := grpc.NewServer( grpc.UnaryInterceptor(LogBodyInterceptor()), )
UnaryClientInterceptor
次にクライアント側の例です。
type UnaryClientInterceptor func( ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption ) error
と定義されているので、この通りの関数を用意します。
それぞれ説明すると
変数 | 意味 |
---|---|
ctx | context。サーバに渡したいmetadataとかセットする |
method | gRPCのメソッド名 |
req | request body |
reply | サーバからのresponse body |
cc | client connectio |
invoker | コールするgRPCメソッド。これを実行するとRPCが実行される |
opts | コール時のオプション。サーバからのresponse header, trailerはここから取得 |
具体的な実装
metadataにトークンを設定してサーバに送る例です。
func ClientIAuthnterceptor() grpc.UnaryClientInterceptor { return func( ctx context.Context, method string, req interface{}, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { md := metadata.Pairs("x-auth-token", "hogehoge") ctx = metadata.NewOutgoingContext(ctx, md) err := invoker(ctx, method, req, reply, cc, opts...) return err } }
ポイント
- invokerの前に書けばgrpcメソッドをコールする前に処理される
- invokerの後に書けばgrpcメソッドをコールした後に処理される
なので以下のような順になります。
func interceptor() { // 2 invoker() // 3 } func main() { // 1 resp, err := someRPCCall() // 4 }
grpcクライアントにセットする方法
こんな感じにDialOption
としてセットしていきます。
conn, err := grpc.Dial( address, grpc.WithInsecure(), grpc.WithUnaryInterceptor(ClientAuthInterceptor()), ) if err != nil { log.Fatal(err) } defer conn.Close()
具体的な用途は?
- ログ
- 認証・認可
- 特定のメソッドだけ何かさせたい時に
- x-request-idなどトレーシングに
などでしょうか。metadataはHTTPでいうHeaderに近いので、同じような役割で使えると思います。
まとめ
Unary Interceptorの基本的な使い方を説明しました。