rpc.register()没生效的根本原因是类型未导出或注册时机错误:arith等结构体及方法名必须首字母大写,且rpc.register()须在net.listen()之前调用;若用registername则客户端必须严格匹配服务名,args/reply字段也须全部导出。

rpc.Register() 为什么没生效?
服务端调用 rpc.Register() 后,客户端仍报 rpc: can't find service method,根本原因不是函数没写对,而是类型没导出或注册时机错。Go 的 RPC 只认导出(首字母大写)的类型和方法,new(Arith) 返回的是 *Arith,但若 Arith 结构体本身未导出(比如写成 arith 小写),整个类型就不可序列化,服务端压根不注册任何方法。
- 结构体、方法名、参数类型、返回字段全部必须首字母大写(导出)
-
rpc.Register()必须在net.Listen()之前调用,否则监听开始后注册无效 - 如果用
rpc.RegisterName("Foo", obj),客户端调用时必须用"Foo.Method",不能用类型名自动推导 - 检查
Args和Quotient等参数结构体是否所有字段都导出(A int✅,a int❌)
rpc.Dial("tcp", addr) 连上了却 Call 卡住或报 unexpected EOF
连接成功不代表调用能通——rpc.Dial("tcp", addr) 只建 TCP 连接,后续 Call() 依赖两端 gob 编码严格一致。常见卡住是因为服务端没真正处理连接,或客户端传参类型和服务端期望不匹配,导致 gob 解码失败,连接被静默关闭。
- 服务端必须对每个新连接调用
rpc.ServeConn(conn)(不能只 accept 一次);漏掉这句,客户端Call()就永远阻塞 - 确保客户端传的参数是地址(
&args),服务端接收参数是指针(*Args),且reply参数也必须是指针 - 服务端方法签名必须为
func(*Args, *Reply) error,少一个星号或类型不对,gob 解码就会失败,表现为unexpected EOF - 不要复用同一个
*rpc.Client实例给多个 goroutine 并发Call(),它不是线程安全的
想用 curl 或浏览器测试 RPC 接口?别试了
net/rpc 默认走纯 TCP + gob 编码,HTTP 只是可选 transport 层,且必须显式启用。直接访问 http://localhost:1234 会 404 或 405,因为服务端没注册任何 HTTP handler,更不会返回 JSON 或 HTML。
- 要支持 HTTP,服务端得调用
http.Handle("/rpc", rpc.DefaultServer.HTTPHandler()),再用http.ListenAndServe() - 客户端必须用
rpc.DialHTTP("http", "http://localhost:1234/rpc"),URL 路径必须和服务端 handler 注册路径完全一致 - 即便走 HTTP,底层仍是 gob 编码,curl 发 raw POST 也解不开;想真用 curl,得换
net/rpc/jsonrpc包,并配jsonrpc.NewClient - Wireshark 抓到的是二进制 gob 流,没法人工 inspect;调试建议加日志打在
ServeConn前后,或改用本地 channel 模拟(如知识库中第一个例子)快速验证逻辑
为什么本地跑通了,部署到服务器就失败?
本地用 localhost 能通,换服务器 IP 就连不上,通常不是防火墙问题,而是服务端 net.Listen("tcp", ":1234") 绑定的是 127.0.0.1(仅本机),对外不可达。
- 改成
net.Listen("tcp", "0.0.0.0:1234")才监听所有网卡,允许外部访问 - 检查服务器安全组/iptables 是否放行目标端口(不只是 ping 通,TCP connect 也要通)
- 客户端
rpc.Dial("tcp", "x.x.x.x:1234")中的 IP 必须是服务端实际监听的外网或内网地址,不能写localhost - 跨机器时,gob 类型定义必须完全一致:相同包路径、相同结构体名、相同字段顺序——哪怕只是重命名一个字段,gob 就 decode 失败
net/rpc 不是“开箱即用”的分布式方案,而是一个需要亲手对齐每一处类型和协议的通信基座。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











