grpc 開發進階 - 使用攔截器 interceptor

現在網上大部分都是 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時,使用 WithUnaryInterceptorWithStreamInterceptor 來設置 interceptor

conn, err := grpc.Dial(
    grpc.WithUnaryInterceptor(unaryInterepotr),
    grpc.WithStreamInterceptor(streamInterceptor)
)

鏈式攔截器

創建 ClientConn時,使用WithChainUnaryInterceptorWithChainStreamInterceptor 可以設置鏈式的 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 時,使用ChainUnaryInterceptorChainStreamInterceptor 可以設置鏈式的 interceptor

func ChainStreamInterceptor(interceptors ...StreamServerInterceptor) ServerOption
func ChainUnaryInterceptor(interceptors ...UnaryServerInterceptor) ServerOption

第一個 interceptor 是最外層的,最後一個爲最內層

使用UnaryInterceptor, StreamInterceptor·添加的interceptor,總是最先執行

example: Interceptor

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章