Go net/rpc 错误不会自动传播,需将 error 作为函数最后一个返回值;自定义错误须实现 gob 编码或用 fmt.Errorf;客户端需区分网络错误与业务错误,并统一响应结构体含 Code/Message 字段。

Go net/rpc 的错误不会自动传播到客户端
RPC 调用中,服务端函数 panic 或返回 error,客户端收到的往往只是 nil 或空响应,而不是预期的错误信息。这是因为 net/rpc 默认只序列化返回值,不强制传递 error——除非你显式把它作为返回值的一部分。
常见错误现象:rpc: server error: runtime error: invalid memory address 这类底层 panic 会被截断,客户端仅看到 rpc: call failed: EOF 或直接超时,根本看不到原始错误堆栈。
- 必须把
error作为函数签名的最后一个返回值,且类型为error(不能是自定义 struct) - 服务端函数不要 panic;若发生异常,应捕获后转为
fmt.Errorf返回 - 客户端调用后必须检查
err参数,而不是只看响应结构体字段
如何让自定义错误在 RPC 中可序列化
net/rpc 底层使用 gob 编码,而 error 接口本身不可直接 gob 编码。如果你返回的是自定义错误类型(比如 *MyAppError),客户端解码时会得到 nil,甚至触发 panic。
解决方法只有两种:要么用标准 errors.New 或 fmt.Errorf 构造错误(它们返回 *errors.errorString,gob 支持),要么自己实现 gob.GobEncoder/GobDecoder 接口。
立即学习“go语言免费学习笔记(深入)”;
- 推荐做法:服务端统一用
fmt.Errorf("code=%d, msg=%s", code, msg)包装错误,并在消息里嵌入错误码 - 避免返回指针型自定义 error(如
&MyError{...}),除非已为该类型注册 gob 编码器:gob.Register(&MyError{}) - 客户端解析错误时,别依赖
errors.Is或errors.As判断具体类型,改用字符串匹配或结构化解析错误消息
客户端调用时如何区分网络错误与业务错误
client.Call 返回的 err 可能来自三处:网络层(连接失败、超时)、RPC 协议层(gob 解码失败)、服务端函数返回的业务错误。这三者需分开处理。
典型误判:if err != nil { log.Fatal(err) } —— 会把临时网络抖动当成致命故障。
- 用
errors.Is(err, context.DeadlineExceeded)或net.ErrClosed等判断是否为网络/上下文错误 - 如果
err == nil,但响应结构体中的Code字段非零(比如resp.Code == 400),说明是服务端返回的业务错误 - 建议在 RPC 响应结构体中固定包含
Code int和Message string字段,统一承载服务端错误语义
使用 jsonrpc 替代 gob 时的错误兼容性陷阱
换成 net/rpc/jsonrpc 后,错误表现更“符合直觉”:服务端 return nil, fmt.Errorf("not found"),客户端 err 就是这个字符串。但这只是表象——JSON-RPC 2.0 规范要求错误对象必须有 code、message、data 字段,而 Go 标准库的 jsonrpc 并不严格遵循它。
- 服务端返回的
error会被编码进 JSON 的error.message,但error.code始终为 0,无法携带业务错误码 - 客户端无法通过标准方式提取结构化错误信息,仍得靠解析
error.Error()字符串 - 若需完整 JSON-RPC 2.0 错误支持(比如
-32602参数错误码),得弃用标准库,改用gorilla/rpc或手写 handler
真正难的不是怎么传错误,而是让上下游对“什么算一个错误”有共识——协议层、序列化格式、错误建模方式,任何一个环节没对齐,排查起来就只剩日志里一行模糊的 rpc: decode error。










