内网服务间通信首选grpc,因其http/2多路复用、protobuf高效序列化、强类型契约及原生流式支持;对外暴露接口必须用http restful,以兼容浏览器、移动端、第三方系统及标准网关中间件。

内部服务间通信,直接用 gRPC;对外暴露接口,必须走 http.ServeMux + RESTful。这不是风格偏好,而是由网络位置、调用方能力、中间件兼容性共同决定的硬约束。
为什么内网服务间通信首选 gRPC
Go 微服务通常部署在 Kubernetes 集群内网,延迟敏感、QPS 高、调用链深——这些场景下 gRPC 的底层机制刚好对症:
-
HTTP/2多路复用:1 个 TCP 连接承载上百并发请求,http.Client的Transport.MaxIdleConnsPerHost不再是瓶颈 - Protobuf 序列化体积只有 JSON 的 ~35%,反序列化耗时约 30%,实测平均延迟下降 40%~60%
- 强类型契约:字段名拼错、类型不匹配在
protoc-gen-go生成阶段就报错,不是运行时 panic - 原生支持流式 RPC:
rpc StreamLogs (Empty) returns (stream LogEntry)这类场景,HTTP/1.1 得靠 SSE 或长轮询硬凑
但注意:grpc-go 默认不启用 keepalive,空闲 2 小时后连接静默断开,现象是突然出现 rpc error: code = Unavailable desc = transport is closing——必须显式配置 keepalive.EnforcementPolicy 和 keepalive.ServerParameters。
HTTP/REST 什么时候不可替代
不是 HTTP 不够快,而是它解决的是另一类问题:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 前端浏览器、iOS/Android App、第三方系统调用你的服务?它们没法直接解析
.proto文件,也不愿处理 TLS 双向认证和二进制帧 - 要过 Nginx、Kong、AWS API Gateway?这些网关只认
Authorization、Cache-Control、ETag等标准头部,grpc.Metadata对它们是黑盒 - 产品或测试同学要快速验证接口?
curl -X POST http://localhost:8080/api/users一行搞定;grpcurl -plaintext localhost:8080 user.UserService/CreateUser得先装工具、写 JSON 输入、处理 TLS 参数
别为了“统一协议”强行把 HTTP 接口包装成 gRPC stub——调试成本、中间件适配成本、客户端接入成本会指数级上升。
混用 HTTP 和 gRPC 的常见翻车点
很多团队在同一个端口上同时跑 http.ServeMux 和 grpc.Server,结果线上流量莫名 404 或返回乱码,根源往往在这几个细节:
- 用
http.Handle("/", handler)拦截所有路径,但gRPC请求路径形如/helloworld.Greeter/SayHello,被 HTTP handler 吞掉,根本没到grpc.Server - 没做路径区分:正确做法是用
http.ServeMux注册具体路径(如/health、/metrics),其余交给grpc.Server处理;或者用grpc-gateway自动生成 REST 接口 - 误把
net/rpc当 gRPC 用:net/rpc基于gob编码、仅限 Go、不支持流、无超时传播、无拦截器——它连微服务通信的门槛都没迈过去 -
*grpc.ClientConn没复用:每次请求都grpc.Dial,瞬间打爆文件描述符和连接数,应全局复用并配合WithBlock()或上下文超时控制
性能优化不能只看协议选型
选了 gRPC 不等于自动高性能。真实瓶颈常藏在配置层:
- 客户端必须用
context.WithTimeout包裹每次调用,否则慢请求卡住 goroutine,引发雪崩 - Proto 字段若需 JSON 兼容(比如通过
grpc-gateway暴露),务必加json_name注解:string user_id = 1 [(google.api.field_behavior) = REQUIRED, (json_name) = "user_id"]; - HTTP/2 虽好,但若服务端启用了 gzip 压缩而客户端没配
grpc.WithDecompressor,会直接报unknown compression format - 连接池不是万能的:
grpc.ClientConn内部已实现连接复用,不需要额外套一层连接池;反而http.Client的Transport配置(如MaxIdleConns)必须精细调优
最易被忽略的,是错误传播方式:gRPC 错误码(codes.NotFound、codes.InvalidArgument)和 HTTP 状态码(404、400)语义不等价,混用时日志告警、监控聚合、前端兜底逻辑全会错位。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










