【GoLang 那點事】gRPC 攔截器那點事,希望幫到你(六)

a_wei發表於2019-09-16
  • 上一篇介紹了gRPC的介面認證,我們客戶端需要實現gRPC提供的介面,然後在服務端業務介面實現中通過metadata獲取認證資訊,進行判斷,那麼當我們有幾十個,幾百個業務介面時,如果都在介面實現中去做,那將是一個噩夢,也不符合DRY(Don't Repeat Yourself)原則,今天一起來看看如何通過gRPC的攔截器做到統一介面認證工作

初識gRPC攔截器

  • gRPC在grpc包中定義了一個變數,如下,這個變數叫做UnaryServerInterceptor(一元服務攔截器)
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, 
        handler UnaryHandler)  (resp interface{}, err error
)
  • 其型別是一個函式,這個函式有,4個入參,兩個出參,介紹如下
  • ctx context.Context 上下文
  • req interface{} 使用者請求的引數
  • info UnaryServerInfo RPC 方法的所有資訊,定義如下
type UnaryServerInfo struct {        
// Server is the service implementation the user provides. 
// This is read-only.        
    Server interface{}        
// FullMethod is the full RPC method string,
// package.service/method.        
    FullMethod string
}
  • handler UnaryHandler RPC方法本身
  • resp interface{} RPC方法執行結果
type UnaryHandler func(ctx context.Context,  req interface{}) (interface{}, error)

如何使用

  • 首先定義一個攔截器
//宣告一個變數
var serverIntercept  grpc.UnaryServerInterceptor
 //為這個變數賦值
serverIntercept = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 
        handler grpc.UnaryHandler) (resp interface{}, err error) {
         //這個方法的具體實現
         err = check(ctx) //許可權校驗
         if err != nil {
              return
         }
         // 校驗通過後繼續處理請求
         return handler(ctx, req)
}
  • 在服務端啟動時將攔截器新增進去
 //將攔截器新增進去
gRpcServer :=grpc.NewServer(
    []grpc.ServerOption{grpc.UnaryInterceptor(serverIntercept)}...
)
  • 如上就是服務端使用攔截器的所有步驟,客戶端在訪問服務端時就會被攔截

如何新增多個攔截器

  • 有人說我看上面程式碼grpc.NewServer是一個可變引數,我傳多個不就好了嗎?整的是這樣嗎,我們來試試,程式碼如下我們新增了兩個攔截器
gRpcServer :=grpc.NewServer(
    []grpc.ServerOption{
    grpc.UnaryInterceptor(serverIntercept1),
    grpc.UnaryInterceptor(serverIntercept2)}...
)
  • 很遺憾,服務端啟動失敗,報錯資訊如下,什麼含義呢,意思是說,這個一元服務攔截器只能設定一個,不能重複,其實從名字就能看出,一元攔截器,就是說只能設定一個攔截器,gRPC有意的阻止攔截器鏈的形式

panic: The unary server interceptor was already set and may not be reset. [recovered]
panic: The unary server interceptor was already set and may not be reset.

  • 那我們如果真的需要攔截器鏈,該如何配置呢,核心思想是遞迴,程式碼如下:
func InterceptChain(intercepts...grpc.UnaryServerInterceptor)grpc.UnaryServerInterceptor{
    //獲取攔截器的長度
    l := len(intercepts)
    //如下我們返回一個攔截器
    return func(ctx context.Context, req interface{},
        info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error){
        //在這個攔截器中,我們做一些操作
        //構造一個鏈
        chain  := func(currentInter grpc.UnaryServerInterceptor,currentHandler grpc.UnaryHandler) 
        grpc.UnaryHandler {
            return func(currentCtx context.Context,
                currentReq interface{})(interface{}, error) {
                return currentInter(
                    currentCtx,
                    currentReq,
                    info,
                    currentHandler)
            }
        }
        //宣告一個handler
        chainHandler := handler
        for i :=  l-1; i >= 0; i-- {
            //遞迴一層一層呼叫
            chainHandler = chain(intercepts[i],chainHandler)
        }
        //返回結果
        return chainHandler(ctx,req)
    }
}

gRPC還有哪些攔截器

統一在grpc包下,其他攔截器如下
  1. type UnaryClientInterceptor

    這是一個客戶端上的攔截器,在客戶端真正發起呼叫之前,進行攔截,這是一個實驗性的api,這是gRPC官方的說法

  2. type StreamClientInterceptor

    在流式客戶端呼叫時,通過攔截clientstream的建立,返回一個自定義的clientstream,可以做一些額外的操作,這是一個實驗性的api,這是gRPC官方的說法

  3. type UnaryServerInterceptor (就是上面我們demo中的攔截器)
  4. type StreamServerInterceptor

    攔截伺服器上流式rpc的執行

gRPC的攔截分類

  • 按功能來分
    1. 一元攔截器 UnaryInterceptor
    2. 流式攔截器 StreamInterceptor
  • 按端來分
    1. 客戶端攔截器 ClientInterceptor
    2. 服務端攔截器 ServerInterceptor
  • 大家平時使用時靈活使用即可,慢慢的就會融會貫通

歡迎大家關注微信公眾號:“golang那點事”,更多精彩期待你的到來

gRPC 攔截器那點事,希望幫到你

那小子阿偉

相關文章