protoc生成的Go代码必须与.proto文件严格一致,否则调用会panic或序列化错乱;字段需显式声明json_name以保证grpc-gateway等工具解析正确,repeated字段对应切片但nil不编码,客户端须复用*grpc.ClientConn,服务端流式响应须持续检查ctx.Err()并判断Send错误。

protoc 生成的 Go 代码必须与 .proto 文件严格一致,否则调用会 panic 或序列化错乱 —— 这是绝大多数 gRPC 调用失败的第一原因。
proto3 定义不匹配 Go 结构体字段名就炸
Go 的 grpc-go 生成器默认把 snake_case 字段转成 CamelCase,但底层仍依赖 json_name 标签做反序列化对齐。一旦手动改了 pb.go 或忽略 json_name,HTTP/JSON 网关(比如 grpc-gateway)或调试工具(如 grpcurl)就会解析失败。
- 所有 message 字段必须显式声明
json_name,例如:string user_id = 1 [json_name = "user_id"]; -
repeated字段对应 Go 切片,但nil切片不会被编码发送,空切片[]string{}才会;服务端收到nil时需主动判空,不能直接 range - 禁止在
.proto中使用optional(proto3 默认全 optional),除非你明确启用了experimental_allow_proto3_optional且所有语言客户端都支持
客户端连接不能每次调用都 grpc.Dial
频繁 grpc.Dial 会触发大量 TCP 握手和 TLS 协商,吞吐直接掉 3–5 倍。Go 的 *grpc.ClientConn 是线程安全、可复用的,但很多人误以为“每次请求都要新连”。
- 全局复用单个
*grpc.ClientConn,初始化时加grpc.WithBlock()仅用于启动期阻塞等待服务就绪,生产环境应设为false并配健康检查 - 超时必须由
context.WithTimeout控制,gRPC 不传播 HTTP-style timeout header,服务端无法靠配置统一限流 - 拦截器(如 auth、logging)要封装成
grpc.UnaryClientInterceptor,但函数体内绝不能panic,否则整个调用链中断 - 进程退出前必须显式调用
conn.Close(),否则 goroutine 和连接泄漏,K8s 下 pod 重启变慢
服务端流式响应不检查 ctx.Err() 就会卡死
用 stream.Send() 推送数据时,若客户端断开、超时或网络拥塞,gRPC 内部缓冲区会堆积,最终返回 rpc error: code = DeadlineExceeded desc = context deadline exceeded 或更隐蔽的 transport: failed to write a frame。
- 每个流式 handler 必须持续检查
stream.Context().Err() != nil,一发现就 break + return - 每次
stream.Send()后必须判断错误:if err := stream.Send(msg); err != nil { return err } - 不要无节制循环发送,尤其当消息体积大或频率高时;必要时用
stream.SetHeader()传元数据协商节奏,或降级为双向流让客户端Recv()确认就绪
grpc.Dial 的 insecure.NewCredentials() 只能用于开发
本地调试用 grpc.WithTransportCredentials(insecure.NewCredentials()) 很方便,但上线后必须换 TLS,否则连接会被中间设备重置、证书校验失败、甚至被注入伪造响应。
立即学习“go语言免费学习笔记(深入)”;
- 生产环境必须用
credentials.NewTLS(tlsConfig),其中tlsConfig.ServerName要与服务端证书 SAN 匹配 - 若走服务发现(Consul / etcd),DNS 名称解析可能绕过 TLS 验证,需显式设置
grpc.WithAuthority("my-service.example.com") - gRPC 的 HTTP/2 连接复用对 TLS 更敏感:单个连接上多个 stream 共享同一 TLS session,一旦证书过期或密钥轮换不及时,整批请求批量失败
.proto 和 Go 运行时之间那层隐式映射,以及连接生命周期和流控逻辑的耦合。写完接口定义后,先用 grpcurl 直连验证字段序列化,再跑集成测试看流式是否及时中断 —— 这两步跳过,后面八成要花半天查 context cancel 没生效或连接没复用。


















