gob编码让RPC响应变慢是因为它写入大量类型元信息,导致序列化体积大、CPU消耗高,尤其在字段多或嵌套深时更明显,实测比JSON慢30–50%,比Protobuf慢2–3倍。
gob 编码会让 RPC 响应变慢gob 是 Go 标准库 net/rpc 的默认编码器,但它不是为高性能网络传输设计的。它会写入大量类型元信息,序列化后体积大、CPU 消耗高,尤其在结构体字段多或嵌套深时更明显。实测中,相同数据用 gob 比 json 多 30–50% 序列化时间,比 protobuf 高出 2–3 倍。
rpc.Register + 默认 gob 编码net/rpc,可替换为 jsonrpc(需手动包装 json.ServerCodec)gRPC 或自定义二进制协议,用 protocol buffers 定义服务和消息gRPC 替代原生 net/rpc 并启用流控gRPC 不仅自带高效 protobuf 编码,还支持连接复用、头部压缩、deadline 控制和内置流控机制。关键不是“换框架”,而是把 net/rpc 中隐式依赖的长连接、重试、超时等逻辑显式收归到 gRPC 的 ClientConn 和 CallOption 中管理。
KeepaliveParams,例如 ServerParameters{MaxConnectionAge: 30 * time.Minute}
context.WithTimeout,否则一次卡死会拖垮整个连接池
认的 gzip 压缩(小包反而增开销),大响应体再按需启用:grpc.UseCompressor(gzip.Name)
conn, _ := grpc.Dial("localhost:8080",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 10 * time.Second,
Timeout: 3 * time.Second,
PermitWithoutStream: true,
}),
)net/rpc 连接池没做对,吞吐量就上不去标准 net/rpc 自身不提供连接池,rpc.Dial 每次新建 TCP 连接,而 rpc.Client 实例也不是线程安全的。常见错误是全局复用一个 *rpc.Client,或每次请求都 Dial + Close —— 前者导致并发阻塞,后者触发频繁三次握手与 TIME_WAIT。
*rpc.Client;改用 sync.Pool 管理已建立的 client 实例http.Serve 或 rpc.ServeConn 复用底层 conn,而非每请求启新 goroutinenet.Conn 检查 Write 是否返回 io.EOF 再决定是否回收proto.Marshal 成为性能瓶颈
即使用了 protobuf,如果响应结构体包含未清理的空切片、冗余字段或嵌套过深的 map,proto.Marshal 仍可能占用 10ms+ CPU 时间。这不是 GC 问题,而是序列化路径上反复反射判断字段有效性所致。
--go_opt=paths=source_relative,避免 import 路径引发的 init 开销proto.CompactTextString 验证字段合法性,提前暴露 nil 指针或非法 enumunsafe.Slice + 手写二进制打包替代 proto.Marshal(仅限字段稳定、无嵌套、无 optional 的场景)真正卡住吞吐的,往往不是网络延迟,而是单次响应里那几毫秒的序列化抖动 —— 它让并发请求排队等待,放大了整体 P99 延迟。