grpc-gateway 是基于 Go HTTP handler 的反向代理,不自动转换协议:它解析 HTTP/JSON 为 protobuf,用本地 gRPC client 转发请求,再将响应编码为 JSON;所有路由和行为依赖 protoc 插件生成的 service.pb.gw.go 及手动调用 RegisterXXXHandlerFromEndpoint。
grpc-gateway 不是“自动转换协议”,而是用 go http handler 做反向代理:它把 incoming http/json 请求解析成 protobuf 消息,再用本地 grpc client 转发给 grpc server,最后把 response 编码回 json —— 整个过程不改 grpc 协议,也不共享连接或序列化逻辑。
proto 中的 google.api.http 注解只是声明,不触发任何运行时行为
你写 get: "/v1/users/{id}" 或 post: "/v1/users" body: "*",这些不会让代码“自动注册路由”或“生成 handler”。它们只被 protoc 插件读取,用于生成 service.pb.gw.go 里的 HTTP 路由绑定逻辑。
- 必须显式
import "google/api/annotations.proto"和import "google/api/http.proto",否则 protoc 报错或忽略注解 -
body: "*"表示整个 JSON body 映射到 message;若只映射部分字段(如body: "user.name"),gateway 会按路径提取并填充,但字段不存在时静默丢弃,不报错 - 路径参数(如
{id})和 query 参数(如?page=1)都需在 proto message 中定义对应字段,否则 gateway 不解析、不传入
RegisterXXXHandlerFromEndpoint 必须手动调用,且不能塞进 Gin/Echo 的路由树
生成的 service.pb.gw.go 里有类似 RegisterGreeterHandlerFromEndpoint 的函数,它负责把 gRPC service 方法注册到 runtime.ServeMux。这个 mux 是独立的 HTTP handler,不是中间件。
- 不能写成
r.POST("/xxx", mux.ServeHTTP)—— 这会让 Gin 丢失 method/path 匹配,所有请求都 fallback 到 404 - 必须用
http.ListenAndServe(":8080", mux)直接启动,或用自定义http.Server{Handler: mux} -
ctx参数不能是已 cancel 的 context(比如从context.WithTimeout创建后超时了),否则注册失败且无提示 -
endpoint必须指向正在运行的 gRPC server 地址(如"localhost:9090"),不能是域名未解析、端口未监听或防火墙拦截的地址
gRPC server 与 gateway 共享端口需靠 http.Server.Handler 分流,而非复用 listener
想让 8080 端口同时响应 REST 和 gRPC 请求,不能起两个 ListenAndServe,也不能让 gRPC server 直接 Serve(lis) —— 否则会抢 socket、断连、503 随机出现。
通过IB网关在Interactive Brokers上投资股票/ETF,使用与okx交易相同的人机确认环节,适用于用户请求检查时
- 正确做法:用
golang.org/x/net/http2+h2c.NewHandler包裹 gRPC server,作为 fallback handler;主http.ServeMux处理 REST,未匹配路径才交给 gRPC - TLS 下注意:HTTP/2 over TLS 要求 ALPN 协商,而 gateway 默认走 HTTP/1.1;若客户端不支持 HTTP/2,gRPC 流量可能被拒绝,但 REST 不受影响
- 别用
grpc.NewServer().Serve(lis)单独监听 —— 它会独占 listener,导致 gateway 无法 accept 连接
protojson 编解码行为与原生 gRPC 不一致,空值、时间、枚举处理要提前对齐
gateway 默认用 google.golang.org/protobuf/encoding/protojson(非已弃用的 jsonpb),但它和 gRPC server 内部使用的二进制编码逻辑不同:字段名默认小驼峰、nil slice/map 输出为 null 而非 []、time.Time 序列化为 RFC3339 字符串而非纳秒整数。
- 若 gRPC server 返回
repeated string tags = 1;为空,gateway 默认输出"tags": null;加UseEnumNumbers: true或EmitUnpopulated: true可调整 - 自定义
protojson.MarshalOptions并传给runtime.WithMarshalerOption,否则无法控制空值、时间格式等细节 - 错误返回:gateway 把 gRPC status.Code 映射为 HTTP 状态码(如
NotFound→ 404),但status.Details()默认不透出,需显式启用runtime.WithForwardResponseOption
最常被跳过的点是:以为 google.api.http 写完就万事大吉,结果跑起来全是 404;或者把 gateway mux 当成普通 handler 塞进框架路由,结果 path 匹配失效。真正起作用的永远是那几行手写的 RegisterXXXHandlerFromEndpoint 和 http.ListenAndServe —— 其他都是生成代码和配置。

















