net/rpc适用于同构Go系统内部轻量级进程间通信,不适合公网暴露、高并发或跨语言场景;其gob序列化脆弱、HTTP传输非标准REST,遇重试/可观测性/鉴权需求时应迁移到gRPC等方案。

Go 的 net/rpc 适合内部服务间轻量通信
它不是为公网暴露、高并发网关或跨语言调用设计的,而是给同构 Go 系统内部“模块拆分后仍想保持函数调用感”的场景用的。比如一个单体应用逐步拆成多个进程(但都用 Go 写),又不想立刻上 gRPC 或 HTTP API,net/rpc 就能快速搭起调用通道。
典型适用场景包括:
- 本地开发环境多进程协作(如主程序 + 日志收集子进程 + 配置热加载守护)
- 公司内网中低 QPS 的管理类服务(如部署触发器调用构建 agent)
- 对延迟敏感但协议无关紧要的监控探针通信(如采集指标后推给聚合节点)
一旦涉及鉴权、流控、重试、超时链路追踪,或者对方是 Python/Java 服务,net/rpc 就会迅速变成维护负担。
gob 编码限制了跨语言和向前兼容能力
net/rpc 默认用 gob 序列化,这是 Go 原生二进制格式,性能不错但完全不兼容其他语言。更关键的是:gob 对结构体字段增删非常脆弱——如果服务端加了个新字段,旧客户端解包可能 panic;反过来,客户端传了新字段,服务端没定义,字段直接被忽略且无提示。
立即学习“go语言免费学习笔记(深入)”;
规避方式有限,实际中建议:
- 所有 RPC 接口参数/返回值类型必须定义在共享
types.go包中,服务端和客户端共用同一份 struct 定义 - 避免使用匿名字段、嵌套指针、
interface{}——gob对这些支持不稳定 - 升级前务必做双向兼容测试,不能只测服务端启动不报错
如果你需要长期演进的接口,或者未来可能换语言实现某端,从第一天就该跳过 net/rpc。
HTTP transport 不等于 REST,别误当 Web API 用
net/rpc 支持通过 HTTP 打隧道(rpc.ServeHTTP),但这只是把 RPC 消息塞进 POST body,并不生成标准 HTTP 状态码、Header 或路径路由。客户端仍需用 rpc.Client 连接,不能用 curl 或浏览器直接调试。
常见误解和后果:
- 以为加了
http.ListenAndServe就能被前端 JS 调用 → 实际会返回405 Method Not Allowed或空响应,因为没处理 OPTIONS/GET - 在 Nginx 后面反代时未配置
proxy_buffering off和proxy_http_version 1.1→ 出现粘包或连接复用失败 - 用
http.DefaultClient发起普通 HTTP 请求去调用 → 得到乱码二进制,因为没走rpc.Client的 gob 解码逻辑
真要对外提供 Web 接口,直接写 http.HandlerFunc 或用 gin/echo;非要 RPC over HTTP,至少加上健康检查 endpoint 和明确的错误包装。
替代方案选型:什么时候该换掉 net/rpc
项目刚起步、只有两个 Go 进程、日均调用量不到 1000 次,net/rpc 是省事的选择。但只要出现以下任一信号,就该评估迁移:
- 开始写
if err != nil { log.Printf("RPC failed: %v", err); time.Sleep(100 * ms) }这类重试逻辑 → 说明你已经在补net/rpc缺失的可靠性机制 - 同事问“怎么用 Postman 测试这个接口”你答不上来 → 暴露了可观测性短板
- 需要传递 trace ID 或透传 auth token →
net/rpc的 Header 机制(context传递)非常隐晦且易漏
平滑过渡建议:先用 gRPC-Go 替换核心服务,保留 net/rpc 作为 legacy adapter;不要试图魔改 net/rpc 加 JSON 支持——它的抽象层根本不为此设计。










