如何使用Golang实现RPC请求拦截_使用中间件处理请求和响应

1次阅读

go 语言 net/rpc 原生不支持拦截,但可通过服务端包装 Handler 或客户端封装 Call 实现轻量拦截;生产环境推荐直接使用原生支持拦截的 gRPC。

如何使用Golang实现RPC请求拦截_使用中间件处理请求和响应

Go 语言标准库net/rpc 本身不支持中间件或拦截机制,但可以通过封装服务端和客户端逻辑,结合自定义编解码器、Server.RegisterName 的包装、或使用更现代的方案(如 gRPC 或第三方 RPC 框架)来实现请求/响应拦截。下面提供两种实用、轻量、原生兼容的方式:

方式一:服务端拦截 —— 包装 Handler 函数

利用 rpc.Server 的可扩展性,在注册方法前对函数做一层包装,实现统一日志、鉴权、耗时统计等。

  • 定义一个通用拦截器类型:
      type Interceptor func(ctx context.Context, method String, req, resp Interface{}) Error
  • 在注册服务时,用反射或手动包装每个方法,将原始 handler 套进拦截逻辑中;
  • 推荐做法:不直接注册原始结构体,而是注册一个代理对象,其方法调用前先执行拦截器,再转发到真实服务:

type InterceptedService Struct {
  svc interface{}
  interceptors []Interceptor
}
func (s *InterceptedService) Call(ctx context.Context, method string, req, resp interface{}) error {
  for _, i := range s.interceptors {
    if err := i(ctx, method, req, resp); err != nil {
      return err
    }
  }
  // 调用真实方法(需配合 reflect 或 codegen 实现)
  return callRealMethod(s.svc, method, req, resp)
}

方式二:客户端拦截 —— 封装 Client.Call

标准 *rpc.Client 是导出的,但 Call 方法不可覆盖。可行做法是封装一个代理 Client 类型:

  • 定义 type InterceptedClient struct { client *rpc.Client; interceptors []ClientInterceptor }
  • 实现自己的 Call 方法,在调用 client.Call 前后插入逻辑(如添加 trace ID、记录开始时间、校验响应字段);
  • 示例拦截器:

func LogInterceptor(method string, req, resp interface{}, start time.Time, err error) {
  log.printf(“[RPC] %s: %v → %v | took %v | err: %v”,
    method, req, resp, time.Since(start), err)
}

进阶:用 gRPC 替代 net/rpc(推荐生产环境)

如果项目允许升级协议,gRPC 原生支持拦截器(UnaryInterceptor / streamInterceptor),语义清晰、生态成熟:

如何使用Golang实现RPC请求拦截_使用中间件处理请求和响应

Avatar AI

AI成像模型,可以从你的照片中生成逼真的4K头像

如何使用Golang实现RPC请求拦截_使用中间件处理请求和响应 92

查看详情 如何使用Golang实现RPC请求拦截_使用中间件处理请求和响应

立即学习go语言免费学习笔记(深入)”;

  • 服务端拦截只需实现 grpc.UnaryServerInterceptor 函数,并传给 grpc.NewServer(opts...)
  • 客户端拦截同理,用 grpc.WithUnaryInterceptor
  • 可轻松集成 prometheus 监控、OpenTelemetry 链路追踪、JWT 验证等;

例如服务端日志拦截器:

func loggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
  log.Printf(“→ %s: %+v”, info.FullMethod, req)
  resp, err := handler(ctx, req)
  log.Printf(“← %s: %+v (err=%v)”, info.FullMethod, resp, err)
  return resp, err
}

小结与建议

  • 原生 net/rpc 拦截需手动包装,适合简单场景或学习目的;
  • 客户端拦截比服务端更容易落地,只需代理 Call
  • 新项目强烈建议直接采用 gRPC,拦截、超时、重试、加密等能力开箱即用;
  • 若必须用 net/rpc 且需强扩展性,可考虑基于 gob 编解码层注入元数据(如在请求体前加 header 字节),但会破坏协议兼容性。

基本上就这些。不复杂但容易忽略的是:拦截逻辑要尽量无副作用、避免阻塞、注意 context 取消传播。

text=ZqhQzanResources