go-zero(九) RPC攔截器的使用

go zero 攔截器詳解

在微服務(wù)架構(gòu)中,攔截器(Interceptor)是一種強(qiáng)大的機(jī)制,用于在 RPC 調(diào)用的生命周期中注入自定義邏輯。go-zero可以設(shè)置多個(gè)攔截器,比如身份驗(yàn)證、日志記錄、請求限流、性能監(jiān)控等,這些都可以通過攔截器實(shí)現(xiàn)

一、攔截器基礎(chǔ)概念

1. 什么是攔截器?

攔截器是 gRPC 框架提供的一種機(jī)制,允許開發(fā)者在 RPC 調(diào)用的不同階段插入自定義邏輯。根據(jù)應(yīng)用場景,攔截器分為兩種類型:

  • 服務(wù)端攔截器(Server Interceptor):在服務(wù)端處理請求前后執(zhí)行自定義邏輯
  • 客戶端攔截器(Client Interceptor):在客戶端發(fā)送請求前后執(zhí)行自定義邏輯

2. 攔截器的主要作用

攔截器能夠在 RPC 調(diào)用鏈中實(shí)現(xiàn)多種功能:

  • 日志記錄:捕獲請求和響應(yīng)信息,便于調(diào)試和監(jiān)控
  • 認(rèn)證授權(quán):驗(yàn)證請求者身份和權(quán)限
  • 請求追蹤:實(shí)現(xiàn)分布式鏈路追蹤
  • 性能度量:記錄請求處理時(shí)間和資源使用情況
  • 錯(cuò)誤處理:統(tǒng)一處理和轉(zhuǎn)換錯(cuò)誤信息
  • 限流熔斷:控制請求流量,防止系統(tǒng)過載
  • 請求參數(shù)校驗(yàn):在服務(wù)層面驗(yàn)證請求參數(shù)

二、攔截器與中間件的區(qū)別與聯(lián)系

在 go-zero 框架中,攔截器和中間件都是用于在請求處理流程中注入自定義邏輯的機(jī)制,但它們各自適用于不同的場景和協(xié)議層。

1. 概念與作用域

中間件 (Middleware):

  • 作用域: 作用于 HTTP 請求處理流程
  • 應(yīng)用場景: REST API 服務(wù)
  • 位置: API 網(wǎng)關(guān)層、HTTP 服務(wù)層
  • 實(shí)現(xiàn)方式: 基于標(biāo)準(zhǔn)庫的 http.Handler 接口

攔截器 (Interceptor):

  • 作用域: 作用于 RPC 調(diào)用流程
  • 應(yīng)用場景: 微服務(wù)間內(nèi)部通信
  • 位置: RPC 服務(wù)層
  • 實(shí)現(xiàn)方式: 基于 gRPC 的攔截器接口

2. 詳細(xì)對比

特性 中間件 (Middleware) 攔截器 (Interceptor)
協(xié)議層 HTTP gRPC (基于HTTP/2)
數(shù)據(jù)格式 多種格式 (JSON, XML, 表單等) Protocol Buffers
調(diào)用方式 同步請求-響應(yīng) 同步/異步、流式
客戶端支持 主要在服務(wù)端使用 同時(shí)支持客戶端和服務(wù)端
執(zhí)行模式 請求-響應(yīng)模式 (洋蔥模型) 請求攔截和響應(yīng)攔截
性能特性 HTTP 協(xié)議開銷較大 gRPC 性能更優(yōu)
適用場景 外部 API 接口、Web 服務(wù) 微服務(wù)內(nèi)部通信、高性能場景

三、服務(wù)端攔截器

服務(wù)端攔截器在 RPC 服務(wù)處理請求前后執(zhí)行,可以用于請求驗(yàn)證、權(quán)限檢查、日志記錄等場景。

AddUnaryInterceptors 是在 gRPC 服務(wù)器初始化時(shí)調(diào)用的方法,用于將一個(gè)或多個(gè)服務(wù)端攔截器注冊到 gRPC 服務(wù)器上.

1. 服務(wù)端攔截器定義

服務(wù)端攔截器必須遵循 gRPC 定義的函數(shù)簽名:

func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error)

各參數(shù)含義:

  • ctx: 請求上下文,包含元數(shù)據(jù)
  • req: 客戶端請求參數(shù)
  • info: RPC 方法的相關(guān)信息,如方法名
  • handler: 實(shí)際處理請求的處理器

2. 實(shí)現(xiàn)服務(wù)攔截器

接下來我們使用兩個(gè),攔截器作為演示

日志攔截器
記錄每個(gè) RPC 請求的詳細(xì)信息,包括方法名、請求參數(shù)、處理時(shí)間等:

func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    // 記錄請求開始時(shí)間
    startTime := time.Now()
    
    // 提取請求方法名
    method := path.Base(info.FullMethod)
    
    // 記錄請求信息
    logx.Infof("Request - Method: %s, Time: %s, Request: %+v", 
        method, startTime.Format(time.RFC3339), req)
    
    // 調(diào)用實(shí)際處理器
    resp, err := handler(ctx, req)
    
    // 計(jì)算處理時(shí)間
    duration := time.Since(startTime)
    
    // 記錄響應(yīng)信息
    if err != nil {
        logx.Errorf("Response - Method: %s, Duration: %s, Error: %v", 
            method, duration, err)
    } else {
        logx.Infof("Response - Method: %s, Duration: %s, Response: %+v", 
            method, duration, resp)
    }
    
    return resp, err
}

限流攔截器

控制服務(wù)端處理請求的速率,防止系統(tǒng)過載:

// 使用令牌桶算法實(shí)現(xiàn)限流
limiter := rate.NewLimiter(rate.Limit(1), 1) //設(shè)置低一點(diǎn),方便測試 每秒1個(gè)請求,突發(fā)最多1個(gè)
   
func RateLimitInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

    if !limiter.Allow() {
        return nil, status.Error(codes.ResourceExhausted, "請求頻率過高,請稍后再試")
    }
    
    return handler(ctx, req)
}

3. 在go-zero中注冊服務(wù)端攔截器

在RPC服務(wù)的main.go文件中注冊攔截器:

func main() {
    flag.Parse()
    
    var c config.Config
    conf.MustLoad(*configFile, &c)
    
    ctx := svc.NewServiceContext(c)
    svr := server.NewUserServer(ctx)
    
    s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
        pb.RegisterUserServer(grpcServer, svr)
        
        if c.Mode == service.DevMode || c.Mode == service.TestMode {
            reflection.Register(grpcServer)
        }
    })
    
    // 注冊多個(gè)服務(wù)端攔截器(按順序執(zhí)行)
    s.AddUnaryInterceptors(
        LoggingInterceptor,
        RateLimitInterceptor, 
    )
    
    defer s.Stop()
    
    fmt.Printf("Starting RPC server at %s...\n", c.ListenOn)
    s.Start()
}

4. 測試攔截器

業(yè)務(wù)邏輯就簡單的使用 goctl rpc new user生成的代碼。

image.png

四、客戶端攔截器

客戶端攔截器在發(fā)起 RPC 調(diào)用前后執(zhí)行,用于請求前的預(yù)處理和響應(yīng)后的后處理。go-zero 提供了 WithUnaryClientInterceptor 選項(xiàng)來配置客戶端攔截器。

1. 客戶端攔截器定義

客戶端攔截器必須遵循 gRPC 定義的函數(shù)簽名:

func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error

各參數(shù)含義:

  • ctx: 請求上下文
  • method: 完整的 RPC 方法名
  • req: 請求參數(shù)
  • reply: 響應(yīng)結(jié)果
  • cc: gRPC 客戶端連接
  • invoker: 實(shí)際執(zhí)行 RPC 調(diào)用的調(diào)用器
  • opts: 調(diào)用選項(xiàng)

2.實(shí)現(xiàn)性能監(jiān)控?cái)r截器

記錄每個(gè) RPC 調(diào)用的耗時(shí)和狀態(tài),便于性能分析:

func MetricsInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
    // 記錄開始時(shí)間
    startTime := time.Now()
    
    // 提取方法名(去除服務(wù)前綴)
    methodName := path.Base(method)
    
    // 調(diào)用 RPC 方法
    err := invoker(ctx, method, req, reply, cc, opts...)
    
    // 計(jì)算耗時(shí)
    duration := time.Since(startTime)
    
    // 記錄指標(biāo)
    status := "success"
    if err != nil {
        status = "error"
    }
    
    // 這里可以集成 Prometheus 等監(jiān)控系統(tǒng)
    logx.Infof("RPC調(diào)用 - 方法: %s, 狀態(tài): %s, 耗時(shí): %v", methodName, status, duration)
    
    return err
}

3. 注冊客戶端攔截器

在 API 服務(wù)的 internal/svc/servicecontext.go 文件中注冊客戶端攔截器:

func NewServiceContext(c config.Config) *ServiceContext {

    // 創(chuàng)建指標(biāo)攔截器
    metricsInterceptor := MetricsInterceptor

    // 配置 RPC 客戶端,添加攔截器鏈
    userRpc := user.NewUser(zrpc.MustNewClient(c.UserRpcConf, 
        zrpc.WithUnaryClientInterceptor(
            metricsInterceptor,  // 先記錄指標(biāo)
        ),
    ))
    
    return &ServiceContext{
        Config: c,
        UserRpc: userRpc,
    }
}

這里就不演示了, 還可以自行拓展 :重試攔截器、超時(shí)攔截器等等。

如果是使用多個(gè)攔截器,要注意調(diào)用順序 ,指標(biāo)記錄>令牌注入>超時(shí)控制>重試

五、攔截器最佳實(shí)踐

1. 設(shè)計(jì)原則

設(shè)計(jì)高效的攔截器應(yīng)遵循以下原則:

  • 單一職責(zé): 每個(gè)攔截器只負(fù)責(zé)一項(xiàng)功能
  • 正交性: 攔截器之間應(yīng)盡量減少耦合
  • 性能優(yōu)先: 攔截器應(yīng)高效執(zhí)行,避免阻塞
  • 異常安全: 攔截器不應(yīng)干擾正常的請求處理流程
  • 可配置性: 攔截器行為應(yīng)可通過配置調(diào)整
  • 可測試性: 攔截器應(yīng)易于單元測試

2. 攔截器鏈順序設(shè)計(jì)

攔截器的執(zhí)行順序?qū)ο到y(tǒng)行為有顯著影響,合理的順序安排如下:

服務(wù)端攔截器順序:

  1. 恢復(fù)處理 (Recover)
  2. 日志記錄 (Logging)
  3. 指標(biāo)收集 (Metrics)
  4. 認(rèn)證授權(quán) (Authentication & Authorization)
  5. 限流熔斷 (Rate Limiting & Circuit Breaking)
  6. 參數(shù)驗(yàn)證 (Validation)
  7. 業(yè)務(wù)處理 (Business Logic)

客戶端攔截器順序:

  1. 指標(biāo)收集 (Metrics)
  2. 日志記錄 (Logging)
  3. 認(rèn)證注入 (Authentication)
  4. 超時(shí)控制 (Timeout)
  5. 重試處理 (Retry)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容