Go gRPC 拦截器实战:在一元拦截器里统一做鉴权、日志与超时
test12026-09-206 次阅读
不想每个 RPC 都写一遍鉴权?交给拦截器
gRPC 的拦截器(interceptor)相当于服务端的全局中间件。一元拦截器签名统一,能在真正调用业务 handler 之前做鉴权、日志、超时、限流,之后又能统一收口响应与耗时。用 ChainUnaryInterceptor 把多个拦截器串成一条链,顺序即执行顺序。
一元拦截器:鉴权 + 链路日志
下面用两个拦截器演示:authInterceptor 从 metadata 取 token 校验,不合法直接返回 Unauthenticated;logInterceptor 包住 handler 记录方法名与耗时。两者通过 ctx 透传用户信息。
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func authInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, _ := metadata.FromIncomingContext(ctx)
tokens := md.Get("authorization")
if len(tokens) == 0 || tokens[0] != "Bearer secret" {
return nil, status.Error(codes.Unauthenticated, "缺少合法凭证")
}
ctx = context.WithValue(ctx, "user", tokens[0])
return handler(ctx, req)
}
func logInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("method=%s duration=%s err=%v", info.FullMethod, time.Since(start), err)
return resp, err
}
func main() {
s := grpc.NewServer(
grpc.ChainUnaryInterceptor(authInterceptor, logInterceptor),
)
// pb.RegisterDemoServer(s, &server{})
// s.Serve(lis)
_ = s
}
把超时和取消也收进拦截器
拦截器里可以给 ctx 加超时,业务 handler 通过 select 监听 ctx.Done() 即可感知取消,避免慢请求拖垮整个服务。下面是一段 handler 侧的取消感知示例。
func (s *server) GetOrder(ctx context.Context, req *pb.OrderReq) (*pb.Order, error) {
select {
case <-time.After(50 * time.Millisecond):
return &pb.Order{Id: req.Id}, nil
case <-ctx.Done():
return nil, status.Error(codes.DeadlineExceeded, ctx.Err().Error())
}
}
工程收益
- 鉴权、日志、超时等横切逻辑只写一次,业务 handler 保持纯净。
- 拦截器链顺序可控,新增能力(限流、trace)只是再串一个函数。
- 错误信息通过 status 统一返回,客户端拿到标准 gRPC 状态码。
T
test1
文章作者
不想每个 RPC 都写一遍鉴权?交给拦截器 gRPC 的拦截器(interceptor)相当于服务端的全局中间件。一元...
- 分类
- 技术
- 发布时间
- 2026-09-20
- 字数
- 约 1973 字
- 阅读
- 6 次
