Go 语言 net/rpc 默认不支持压缩,但可通过包装 net.Conn 实现 gzip 压缩传输;需服务端用 gzip.NewReader、客户端用 gzip.NewWriter,并统一压缩级别、避免小消息压缩,或直接采用原生支持 gzip 的 gRPC。

Go 语言标准库的 net/rpc 默认不支持消息压缩,但可以通过包装底层连接(如 net.Conn)实现请求和响应的自动压缩传输,从而显著降低带宽消耗。关键在于在客户端和服务端分别对数据流进行压缩/解压,且需保证两端算法一致、协议兼容。
使用 gzip 压缩底层连接
最常用且高效的方式是用 gzip.Reader 和 gzip.Writer 包装 TCP 连接。服务端接收时解压,客户端发送时压缩——但注意:net/rpc 的 HTTP 模式和自定义 TCP 模式处理方式不同,推荐使用纯 TCP 方式以获得完全控制权。
- 服务端:监听后,对每个新连接启动 goroutine,用
gzip.NewReader(conn)包装读取器,再传给rpc.ServeConn - 客户端:建立连接后,用
gzip.NewWriter(conn)包装写入器,再调用rpc.NewClient并替换其内部的WriteCloser - 注意:
gzip.Writer需手动调用Flush()或Close()才会真正写出压缩数据,否则 RPC 消息可能卡住
封装可压缩的 RPC 客户端与服务端
为避免每次手动包装,可封装一个支持压缩的 CompressClient 和 CompressServer。核心是实现自定义的 io.ReadWriteCloser,内部持有一个原始连接和一个 gzip writer/reader,并重写 Read/Write/Close 方法。
- 服务端示例:在
accept循环中,对每个conn构造&compressConn{conn: conn},其中Read调用gzip.NewReader(conn).Read - 客户端示例:构造连接后,传入
&compressConn{conn: conn},其Write方法用gzip.NewWriter缓冲并自动Flush - 务必统一设置 gzip 级别(如
gzip.BestSpeed),兼顾性能与压缩率
兼容性与边界注意事项
RPC 压缩不是透明的,需确保两端行为严格对齐,否则会出现粘包、EOF 或解压失败等问题。
立即学习“go语言免费学习笔记(深入)”;
- 不要混用压缩与非压缩客户端/服务端;建议在连接建立初期交换能力标识(例如先发一个带
Compressed:true的握手 header) - RPC 消息本身有固定格式(如 length-prefix + JSON/GOB),压缩必须作用于整个消息体,不能只压 payload,否则长度字段失效
- 小消息(如 字节长度超过 512B 时才启用压缩
替代方案:使用 gRPC + gzip 编码
若项目允许升级协议,gRPC 原生支持 gzip 编码(通过 grpc.UseCompressor 和 grpc.WithCompressor)。它在 HTTP/2 层自动处理压缩协商与透传,比手撸更健壮,也支持 per-call 控制。
- 服务端注册:调用
grpc.NewServer(grpc.KeepaliveParams(...), grpc.UseCompressor(gzip.Compressor)) - 客户端 Dial:使用
grpc.Dial(..., grpc.WithCompressor(gzip.Compressor)) - 还可结合
grpc.EmptyCallOption实现按需压缩,比如只对大文件上传启用










