現在網上大部分都是 grpc 相關的介紹,真正涉及到 grpc 的配置使用的文章還是比較少的
所以本系列着重介紹 grpc 開發時可以能會用到的一些配置
攔截器在作用於每一個 RPC 調用,通常用來做日誌,認證,metric 等等
interfactor 分爲兩種
- unary interceptor 攔截 unary(一元) RPC 調用
- stream interceptor 處理 stream RPC
client 和 server 端需要單獨設置他們的 interceptor
客戶端
UnaryClientInterceptor
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
可以通過參數獲取 context
, method 名稱
,發送的請求
, CallOption
用戶可以根據這些信息,來改變調用或者做一些其他處理
interceptor 實現可以分爲三步,預處理
,調用(invoker)RPC 方法
,調用後處理
預處理完成後,通過 invoker 來調用 RPC 方法
err := invoker(ctx, method, req, reply, cc, opts...)
調用RPC後,用戶同樣也可以處理返回的響應和錯誤
StreamClientInterceptor
type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)
stream interceptor 是攔截用戶在 stream 上的操作,返回給用戶自定義的 ClientStream
通過調用streamer
可以獲得 ClientStream
, 包裝ClientStream
並重載他的 RecvMsg
和 ·SendMsg
方法,即可做一些攔截處理了
最後將包裝好的 ClientStream
返回給客戶
type wrappedStream struct{
grpc.ClientStream
}
func (w *wrappedStream) RecvMsg(m interface{})error{
log.Printf("Receive a message (Type: %T)", m)
return w.ClientStream.RecvMsg(m)
}
func (w *wrappedStream)SendMsg(m interface{})error{
log.Printf("Send a message (Type: %T)", m)
return w.ClientStream.SendMsg(m)
}
func streamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption)(grpc.ClientStream, error){
// do soming
// return ClientStream
s , err := streamer(ctx, desc, cc, method, opts...)
if err != nil{
return nil, err
}
return &wrappedStream{s}, nil
}
配置攔截器
創建 ClientConn
時,使用 WithUnaryInterceptor 和 WithStreamInterceptor 來設置 interceptor
conn, err := grpc.Dial(
grpc.WithUnaryInterceptor(unaryInterepotr),
grpc.WithStreamInterceptor(streamInterceptor)
)
鏈式攔截器
創建 ClientConn
時,使用WithChainUnaryInterceptor 和 WithChainStreamInterceptor 可以設置鏈式的 interceptor
func WithChainStreamInterceptor(interceptors ...StreamClientInterceptor) DialOption
func WithChainUnaryInterceptor(interceptors ...UnaryClientInterceptor) DialOption
第一個 interceptor
是最外層的,最後一個爲最內層
使用WithUnaryInterceptor
, WithChainStreamInterceptor·
添加的interceptor,總是最先執行
服務端
UnaryServerInterceptor
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
處理和作用於 client 類似
func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler)(interface{}, error){
// do somthing
m, err := handler(ctx, req)
if err != nil{
log.Printf("RPC failed: %v", err)
}
return m, err
StreamServerInteceptor
type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
與客戶端處理類似, 封裝 ServerStream
,並覆蓋 RecvMsg
,SendMsg
方法
調用 handler 需要傳遞自定義的 ServerStream
type StreamHandler func(srv interface{}, stream ServerStream) error
配置攔截器
在調用 grpc.NewServer
時通過 UnaryInterceptor, StreamInterceptor 來配置 interceptor
s := grpc.NewServer(
grpc.UnaryInterceptor(unaryInterceptor),
grpc.StreamInterceptor(streamInterceptor)
)
鏈式攔截器
調用 grpc.NewServer
時,使用ChainUnaryInterceptor 和 ChainStreamInterceptor 可以設置鏈式的 interceptor
func ChainStreamInterceptor(interceptors ...StreamServerInterceptor) ServerOption
func ChainUnaryInterceptor(interceptors ...UnaryServerInterceptor) ServerOption
第一個 interceptor
是最外層的,最後一個爲最內層
使用UnaryInterceptor
, StreamInterceptor·
添加的interceptor,總是最先執行
example: Interceptor