概要
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の基本的な使い方を説明しました。