- 上一篇介紹了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)
}
}
- github上已經有這麼一個專案,如下
- https://github.com/grpc-ecosystem/go-grpc-...
- 這個專案提供了攔截器的interceptor鏈式的功能,還有其它一些功能,大家可以去學習學習。
gRPC還有哪些攔截器
統一在grpc包下,其他攔截器如下
- type UnaryClientInterceptor
這是一個客戶端上的攔截器,在客戶端真正發起呼叫之前,進行攔截,這是一個實驗性的api,這是gRPC官方的說法
- type StreamClientInterceptor
在流式客戶端呼叫時,通過攔截clientstream的建立,返回一個自定義的clientstream,可以做一些額外的操作,這是一個實驗性的api,這是gRPC官方的說法
- type UnaryServerInterceptor (就是上面我們demo中的攔截器)
- type StreamServerInterceptor
攔截伺服器上流式rpc的執行
gRPC的攔截分類
- 按功能來分
- 一元攔截器 UnaryInterceptor
- 流式攔截器 StreamInterceptor
- 按端來分
- 客戶端攔截器 ClientInterceptor
- 服務端攔截器 ServerInterceptor
- 大家平時使用時靈活使用即可,慢慢的就會融會貫通
歡迎大家關注微信公眾號:“golang那點事”,更多精彩期待你的到來