net/rpc默认无连接池,因rpc.dial每次新建tcp连接且不复用,rpc.client无回收接口,close即断连;必须手动池化net.conn并复用,而grpc的*grpc.clientconn虽内置连接池但需显式调优参数。

为什么 net/rpc 默认连接池不存在,必须自己造
Go 标准库 net/rpc 的 rpc.Dial 每次都新建 net.Conn,不复用、不缓存、不池化。它甚至没提供 Close 以外的回收接口,rpc.Client 用完就 client.Close(),底层 TCP 连接直接断开——这意味着你无法“归还连接”,自然谈不上连接池。
常见错误现象:压测时看到大量 TIME_WAIT、CPU 花在 connect() 系统调用上、QPS 上不去但机器负载不高。
- 别试图包装
rpc.Client实例进sync.Pool:它内部持有已关闭的conn,取出来直接 panic - 真正可行的是池化
net.Conn,再对每个连接调用rpc.NewClient构建临时 client - 必须自己实现
Put逻辑:调用完不能conn.Close(),而要检查conn.RemoteAddr()是否有效,再决定是否放回池 - 空闲连接超时建议设为
30s ~ 90s,太短起不到复用作用,太长可能被中间设备(如 NAT、LB)静默断连
gRPC 的 *grpc.ClientConn 就是连接池,但默认参数太保守
grpc.ClientConn 内置连接池,但它不是“开箱即用”的高性能配置。默认只允许每个 target 最多 100 个空闲连接,且无健康检查,失败后不重试——这在高并发下等于没池。
关键参数必须显式覆盖:
-
grpc.WithTransportCredentials(insecure.NewCredentials())或 TLS 配置,避免每次 dial 都重握手 -
grpc.WithConnectParams(grpc.ConnectParams{MinConnectTimeout: 20 * time.Second})防止瞬时抖动触发频繁重连 -
grpc.WithKeepaliveParams(keepalive.KeepaliveParams{Time: 30 * time.Second})主动保活,对抗 LB 断连 grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(32 避免大 response 触发 <code>rpc error: code = ResourceExhausted desc = grpc: received message larger than max...
Protobuf 不是“换序列化”那么简单,proto 定义直接影响性能
从 gob 切到 protobuf 能降 40%+ 序列化耗时,但如果你的 .proto 文件写得随意,性能收益会打折扣甚至翻车。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
容易踩的坑:
- 嵌套层级超过 5 层:protobuf 解析是递归的,栈深度大增,GC 压力明显上升
- 字段类型滥用:
bytes字段传图片或视频片段?默认MaxRecvMsgSize=4MB,超限直接报错,且内存分配不可控 - 未启用
json_name选项却依赖 JSON API:导致字段名大小写不一致,前端反复适配 - 服务端用
proto.MarshalOptions{Deterministic: true},客户端却没配对应反序列化选项:同一 proto 结构序列化结果不一致,diff 工具失效
不要在 proto 里塞业务逻辑,codec 复用比你想象中更关键
很多人以为换了 protobuf 就一劳永逸,其实 proto.Message 实例本身是无状态的,但编解码过程中的 buffer 分配、反射调用、字段校验都会成为瓶颈。
实操建议:
- 用
sync.Pool缓存*YourRequest和*YourResponse实例,避免每次调用 new 对象 - 禁用
protoc-gen-go生成代码里的XXX_反射方法(如XXX_MessageName),除非你真需要动态 schema - 如果 RPC 方法固定、字段稳定,手写扁平结构体 +
binary.Read/Write可能比 protobuf 更快(尤其小包场景) - gRPC 流式调用下,务必复用同一个
proto.Buffer实例,否则 GC 频繁触发 STW
连接池和序列化不是两个孤立优化点:连接复用减少 handshake 开销,而高效序列化降低单次 call 的 CPU 占用;但若 proto 定义臃肿,再好的连接池也扛不住 decode 阶段的卡顿。真正卡点往往藏在第 3 层嵌套字段的 unmarshal 耗时里,而不是你怀疑的网络层。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










