net/rpc 是 go 标准库轻量级 rpc 框架,需严格满足签名(func(t, args, *reply) error)、导出(字段首字母大写)和编码一致性(gob 要求两端结构体定义完全相同)三重约束,否则静默失败;注册与 http handler 必须匹配,dialhttp 第二参数须为完整 url,不支持跨语言,超时与错误需手动兜底。

net/rpc 是 Go 标准库提供的轻量级 RPC 框架,它不抽象传输层,只做序列化和方法路由。想让它正常工作,必须严格满足签名、导出、编码一致性三重约束——不满足任一条件,调用就会静默失败或卡死,而不是报明确错误。
方法签名必须是 func(*T, *Args, *Reply) error
这是 net/rpc 反射注册时的硬性过滤规则,漏掉任意一个细节,方法就“注册成功但不可调用”:
- 接收者必须是指针类型(
func(t *Arith)),写成func(t Arith)不报错,但客户端调用返回rpc: can't find service method -
Args和Reply都必须是指针(*Args,*Reply),传值类型会被跳过 - 两个参数之外不能有额外参数,返回值只能是
error,多一个int或少一个error都无效 - 结构体字段必须首字母大写(导出),否则
gob编码时字段为空,客户端收到EOF或零值
服务注册与 HTTP handler 必须匹配
很多人用 rpc.Register() 注册,再用 http.Serve(l, nil) 启动,结果客户端 rpc.DialHTTP 总是 404 或 unexpected EOF——根本原因是 handler 绑定错了对象:
-
rpc.Register()默认注册到rpc.DefaultServer,而http.Serve不会自动挂载它的 handler - 正确做法是显式调用
http.Handle("/rpc", rpc.DefaultServer.HTTPHandler()),或改用rpc.HandleHTTP()(它内部已注册好) - 如果用了自定义
rpc.NewServer(),就必须用server.HTTPHandler(),且http.Handle路径要和客户端 DialHTTP 的 URL 完全一致(比如"http://localhost:8080/rpc") -
rpc.DialHTTP("tcp", "localhost:8080/rpc")是错的:第二个参数必须是完整 URL,不是 host+path;正确写法是rpc.DialHTTP("tcp", "http://localhost:8080/rpc")
gob 编码要求两端结构体定义完全一致
net/rpc 默认用 gob,它不依赖 JSON tag 或字段别名,而是靠包路径 + 类型名 + 字段顺序 + 导出状态做类型校验。只要有一处不一致,就解码失败:
- 客户端和服务端的
Args结构体必须在同一个包里定义,或至少包路径、字段名、字段顺序、是否导出完全相同 - 哪怕只是服务端加了个未导出字段、或客户端用了不同包的同名 struct,
gob就会报gob: type not found或直接卡住 - 调试时可临时换用
jsonrpc:服务端用jsonrpc.ServeConn,客户端用jsonrpc.Dial,JSON 更易 inspect,但注意字段需匹配json:tag - 跨语言场景直接放弃
net/rpc——它天生只支持 Go-to-Go,不是配置问题,是协议设计如此
超时和错误处理得自己兜底
net/rpc 不原生支持 context 或调用级超时,所有阻塞都来自底层连接:
- 连接超时要用
net.DialTimeout("tcp", addr, 5*time.Second),再传给rpc.NewClient(conn) - 调用超时没有内置机制,常见做法是启动 goroutine +
select+time.After包裹client.Call - 服务端方法 panic 会导致连接断开,必须在每个 RPC 方法内加
defer func() { recover() }(),否则整个 listener 会退出 - 客户端
Call返回的error可能是网络层(连接失败)、协议层(method not found)或业务层(服务端返回的 error),需分类处理
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











