先装 protoc(macos 用 brew install protobuf,ubuntu 用 apt-get install protobuf-compiler),再依次 go install protoc-gen-go、protoc-gen-go-grpc、protoc-gen-grpc-gateway,然后导出 path="$path:$(go env gopath)/bin",并手动下载 google/api/annotations.proto 到 proto/google 目录。

怎么装 protoc 和 gateway 插件才不报错
装错顺序或 PATH 没配对,protoc 一跑就提示 protoc-gen-go-grpc: plugin not found 或 file not found: google/api/annotations.proto——这不是你代码的问题,是环境没搭好。
- 先装
protoc:macOS 用brew install protobuf,Ubuntu 用sudo apt-get install -y protobuf-compiler,别用源码编译,容易漏protoc-gen-*依赖 - 再装 Go 插件(注意版本对齐):
go install google.golang.org/protobuf/cmd/protoc-gen-go@latestgo install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latestgo install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest - 必须加环境变量:
export PATH="$PATH:$(go env GOPATH)/bin",否则protoc根本找不到插件;验证方式是直接敲protoc-gen-go-grpc --version -
google/api/annotations.proto不是 Go 包,是 proto 文件,得手动下载或用buf管理依赖;最省事的是从 GitHub 官方 repo 下载整个google目录,放到项目根目录的proto/google下,然后-I proto引入
proto 里加 HTTP 映射,哪些字段必须写
google.api.http 不是可选装饰,是 gateway 路由的唯一依据。漏掉 body: "*" 或写错 method,会导致请求 404 或 500,且错误日志极不友好。
- 必须显式声明 HTTP 方法和路径,比如:
post: "/v1/hello";get对应无 body 的查询,post/put必须配body: "*"(表示整个 message 当 JSON body 解析) - 如果想把某个字段当 URL path 参数,比如
get: "/v1/users/{id}",那id字段必须在 request message 里定义,且类型不能是int32以外的复杂类型(gateway v2 对嵌套 path 支持弱) - 别在
option (google.api.http)里写additional_bindings除非真需要多路由映射同一 rpc;它会让生成的.gw.pb.go多出冗余逻辑,还容易和主 binding 冲突 - message 字段名用
snake_case(如user_id),gateway 默认按原样转 JSON key;若要前端用camelCase,得在 struct tag 加json:"userId",但注意:这仅影响 JSON 序列化,不影响 gateway 路由解析
生成 .gw.pb.go 后,启动网关服务常踩的坑
生成完代码,照着文档写几行 runtime.NewServeMux() 就跑,结果 curl 返回空、503、或 panic 报 failed to dial target——问题几乎都出在 mux 注册和 gRPC 连接上。
- 网关本质是反向代理,必须连到正在运行的 gRPC server;别用
localhost:9090硬编码,推荐传参或环境变量,比如grpcServerAddr := os.Getenv("GRPC_ADDR") -
runtime.WithInsecure()只能在开发环境开;生产必须配runtime.WithTransportCredentials(credentials.NewTLS(...)),否则 gateway 会拒绝连接 - 注册 handler 顺序很重要:先
gwMux.HandlePath("POST", "/v1/hello", ...),再http.ListenAndServe(":8080", gwMux);但更稳妥的是用gwMux := runtime.NewServeMux(runtime.WithErrorHandler(yourErrHandler)),自定义错误才能看到真实失败原因 - 别忽略 reflection 服务:gRPC server 端要加
reflection.Register(grpcServer),否则某些 gateway 调试工具(如 grpcurl)无法获取 service 描述
HTTP 请求怎么透传 metadata 到 gRPC 后端
前端带了 Authorization: Bearer xxx,后端却收不到——因为 gateway 默认不转发 header,所有 header 都被过滤,只留白名单里的几个(如 Content-Type)。
- header 名会被自动加前缀
Grpc-Metadata-,比如前端传X-User-ID: 123,后端收到的是Grpc-Metadata-X-User-ID;所以后端要用metadata.Pairs("x-user-id", "123")才能取到 - 要在 gateway 初始化时显式放行:用
runtime.WithMetadata(func(ctx context.Context, req *http.Request) metadata.MD { return metadata.Pairs("x-user-id", req.Header.Get("X-User-ID")) }) - 如果 header 值含空格或特殊字符(如 JWT token),务必做 URL decode;gateway 不自动 decode,直接透传会导致后端解析失败
- 别在 gateway 层做鉴权逻辑;它只是协议转换层,业务校验一律交给 gRPC service 实现,否则违反分层职责,也难测难 debug
gateway 最容易被忽略的一点:它不处理重试、超时、熔断这些微服务治理能力。这些得靠上层网关(如 Envoy)或 client SDK 自己补。把它当成“协议翻译器”,而不是“智能代理”,心态就稳了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











