net/rpc 不支持跨语言调用,且默认无超时、重试、连接池;方法注册需满足导出、参数为指针、返回值唯一且为error;TCP模式需goroutine循环ServeConn;HTTP模式须显式传mux且仅响应POST;跨语言应选gRPC或REST而非魔改gob。

net/rpc 是 Go 标准库中实现轻量级 RPC 的核心包,但它**不支持跨语言调用**,也不默认提供超时、重试或连接池能力。如果你只是做本地服务间快速验证或内部小规模通信,它够用;但一旦涉及生产环境、多语言协作或稳定性要求,就得换方案。
为什么 rpc.Register 失败却没报错?
常见现象:调用 rpc.Register(arith) 后服务启动成功,但客户端始终提示 method not found 或 panic。
根本原因不是注册失败,而是方法签名不符合 net/rpc 的硬性约束:
- 方法必须是导出的(首字母大写),比如
Multiply,不能是multiply - 第一个参数必须是指针类型(
*Args),不能是值类型Args - 第二个参数也必须是指针(
*int或*Quotient),用于反向写入结果 - 返回值必须是
error类型,且只能有一个返回值
漏掉任意一条,方法就不会被注册进服务表——rpc.Register 本身不校验,只静默跳过,这是最容易踩的坑。
rpc.Dial 连上了却 c.Call 报 EOF?
典型错误信息:rpc: can't decode response body: EOF,常发生在服务端提前关闭连接或未正确处理连接生命周期时。
立即学习“go语言免费学习笔记(深入)”;
标准 net/rpc TCP 模式下,每个连接只能服务一次请求(除非用 rpc.ServeConn 循环读取)。常见误写:
- 服务端用
listener.Accept()只 accept 一次,然后直接rpc.ServeConn(conn)—— 这样只处理一个请求就退出 - 没加
for循环监听新连接,导致后续客户端连上后无 handler - 客户端
c.Call后没 close 连接,而服务端又没设 idle timeout,连接卡住
正确做法是:服务端必须在 Accept 循环里为每个新连接启一个 goroutine 调用 rpc.ServeConn。
HTTP 模式下为什么访问 /rpc 返回 404?
调用 rpc.HandleHTTP() 后,必须把 HTTP mux 显式传给 http.Serve,否则 handler 不生效。
错误写法:http.ListenAndServe(":1234", nil) —— nil 表示用默认 mux,但 rpc.HandleHTTP() 注册的是 http.DefaultServeMux 的子路径,而 Go 1.22+ 默认禁用全局 mux,容易失效。
稳妥写法:
rpc.HandleHTTP() http.Serve(l, nil) // 注意:l 是 net.Listener,不是 ":1234"
或者更明确地:
mux := http.NewServeMux() rpc.HandleHTTP() http.Serve(l, mux)
另外,net/rpc 的 HTTP 模式只响应 POST 请求,且 Content-Type 必须是 application/gob(默认)或 application/json(需用 jsonrpc 包),GET 访问 /rpc 会 404 是正常行为。
想跨语言?别碰 encoding/gob
net/rpc 默认用 gob 编解码,而 gob 是 Go 专属格式,PHP/Python/Java 都无法原生解析。
如果真需要跨语言,有两个现实选择:
- 改用
net/rpc/jsonrpc包,它基于 TCP + JSON,至少 PHP/Python 能手写解析;但注意它不支持 HTTP,且没有内置超时控制 - 放弃
net/rpc,直接上 gRPC(protobuf + HTTP/2)或 REST + JSON,这才是工业级跨语言 RPC 的通用路径
强行在 net/rpc 上魔改编码器,代价远高于换方案。
最易被忽略的一点:所有参数和返回结构体的字段都必须是导出的(大写字母开头),哪怕你用的是 jsonrpc——JSON 解析器依赖反射,非导出字段会被忽略,导致字段为空或零值,而错误往往静默发生。


















