Gob 不能直接用于跨语言 RPC,因其嵌入 Go 类型信息(如字段名、包路径),其他语言无对应运行时无法解析;字段须首字母大写才导出;不支持兼容性演进;体积大、性能差。

默认 Gob 为什么不能直接用于跨语言 RPC
Go 的 net/rpc 默认用 gob 编码,它会把 Go 类型信息(如 struct 字段名、包路径)一并打包,导致其他语言根本无法识别。这不是“解不开”,而是协议层面不兼容——Python 或 Java 收到的字节流里混着 reflect.Type 的二进制描述,没对应运行时就直接 panic。
- 字段必须首字母大写(可导出),否则
gob直接忽略,连空值都不会传 - 不支持向前/向后兼容:服务端加个新字段,旧客户端反序列化会失败(不像 Protobuf 的
optional字段可跳过) - 编码体积比 Protobuf 大 40%~60%,实测千次编解码平均
750 B/op,而 Protobuf 只要420 B/op
JSON-RPC 是最简单的跨语言方案,但别在高频调用里用
Go 标准库提供 net/rpc/jsonrpc,只需把 jsonrpc.NewServerCodec(conn) 传给 rpc.ServeConn 就能跑起来,Python/Node.js 都有成熟 JSON-RPC 客户端,调试也方便——curl 一把就能发请求。
- 性能代价明显:标准
encoding/json依赖反射,千次编解码约8500 ns/op,是 Protobuf 的 9 倍多 - 字符串字段越多、嵌套越深,性能跌得越狠;
time.Time默认转成 RFC3339 字符串,占空间且解析慢 - 连接必须是长连接(TCP),不能走 HTTP —— 如果你误用
http.Post发 JSON 到 RPC 端口,会卡住或返回乱码
Protobuf + gRPC 不是“可选优化”,而是生产环境事实标准
如果你的服务要长期演进、对接其他语言、或 QPS 超过几百,直接上 grpc-go + .proto 是唯一合理选择。它不是“换了个序列化库”,而是整套通信模型升级:HTTP/2 多路复用、内置流控、自动压缩、强类型校验。
- 定义
service和message后,protoc生成的代码不含反射,字段访问是纯内存偏移,Unmarshal几乎不分配堆内存 - 启用 gzip 压缩只需两行:
grpc.WithCompressor(gzip.NewGZIPCompressor())(客户端)、grpc.WithDecompressor(gzip.NewGZIPDecompressor())(服务端) - 注意版本陷阱:用
google.golang.org/protobuf(v2)替代老的github.com/golang/protobuf(v1),后者已归档,且 v2 的MarshalOptions支持Deterministic: true控制字节序
自定义 ServerCodec 适合什么场景?
只有当你必须沿用 net/rpc 框架(比如老系统耦合太深),又想摆脱 Gob 时,才需要实现 rpc.ServerCodec。这不是“高级玩法”,而是妥协方案——你得自己处理分帧、错误传播、连接生命周期,稍有不慎就会卡死连接。
立即学习“go语言免费学习笔记(深入)”;
- 用
github.com/vmihailenco/msgpack/v5是最轻量的选择:无需 IDL,结构体加msgpack:"name"标签就行,性能接近 Protobuf(1300 ns/op) - 千万别在
ReadRequestHeader里做阻塞 IO;WriteResponse必须完整写出 header + body,少一个字节对方就 hang 住 - 如果还要加 AES 加密或 LZ4 压缩,务必在
ServerCodec外层包装bufio.Reader/Writer,否则msgpack的流式读取会出错
.proto 文件谁来维护、字段变更要不要 breaking change 检查、error code 怎么映射到 gRPC status。这些比 ns/op 数字重要得多。










