Go原生net/rpc参数传递有硬性约束:方法签名必须为func(T, Args, *Reply) error,三参数全指针且顺序固定;Args和Reply结构体所有字段须首字母大写导出;传值或字段未导出将导致注册失败或调用panic。

Go 原生 net/rpc 对参数传递有硬性约束,不满足就直接 panic 或静默失败——这不是配置问题,是反射签名校验机制决定的。必须严格按规范定义结构体和方法签名,否则服务注册或调用时根本走不到业务逻辑。
参数和返回值必须是指针类型
客户端传参、服务端写回结果,都要求用指针。传值(如 Args)会触发反射校验失败,报 rpc: method has wrong number of ins: 1 或类似错误。
-
func (*Service) Method(args *Args, reply *Reply) error✅ 正确 -
func (*Service) Method(args Args, reply Reply) error❌ 注册即失败 -
func (*Service) Method(args *Args, reply *int) error❌ reply 类型必须是结构体指针(含导出字段),*int不被接受
结构体字段必须全部导出且可序列化
net/rpc 用 gob 或 jsonrpc 编解码,只处理首字母大写的导出字段。未导出字段(如 id int)会被忽略,甚至在 gob 中导致 unexpected EOF。
- 所有字段名必须大写,例如
UserID int,不能是userId int - 嵌套结构体也需导出,例如
User *UserDetail中UserDetail的字段也要大写 - 不支持 map、slice 等内建类型作为顶层参数;若必须用,需包装成导出结构体字段,如
Data []string→Items []string
方法签名必须严格匹配三段式
每个 RPC 方法必须且只能有三个参数:接收器指针、请求参数指针、响应结果指针,返回值必须是 error。少一个、多一个、类型错位,都会在 rpc.Register() 阶段被拒绝。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
立即学习“go语言免费学习笔记(深入)”;
- 错误示例:
func (*S) M(a *A) error(缺 reply)、func (*S) M(ctx context.Context, a *A, r *R) error(多 ctx)、func (*S) M(a *A, r *R) (int, error)(多返回值) - 正确签名唯一形式:
func (*T) MethodName(argType *ArgType, replyType *ReplyType) error - 方法名本身也必须导出(首字母大写),否则
rpc包反射时根本看不到它
跨语言互通必须放弃 gob,改用 JSON 或 Protobuf
gob 是 Go 专有格式,无跨语言实现,也不稳定——Go 1.19+ 对未导出字段的编码行为已变更,老客户端可能 decode 失败。要真正做协议标准化治理,必须切换序列化层。
- 用
net/rpc/jsonrpc:需改用 TCP 连接(jsonrpc.ServeConn),不支持 HTTP;字段类型受限(如不支持time.Time直接序列化,需转为字符串) - 更推荐弃用原生
net/rpc,改用 gRPC + Protobuf:天然支持多语言、Metadata、流控、超时,且.proto文件本身就是协议契约 - 若坚持轻量方案,可用
msgpack替代gob,但需自行约定 schema 演进规则,没有 Protobuf 那种字段编号兼容机制
最容易被忽略的是:即使你把结构体字段全改成大写、参数全加星号、方法签名完全合规,只要服务端用 gob 而客户端用 Python 写了个“差不多”的 JSON 解析器,就大概率在某个字段类型变更后开始丢数据——协议标准化不是只写对结构体,而是要锁定序列化格式、字段语义、版本升级路径。这一步绕不开工具链收敛,比如强制所有服务用 protoc-gen-go 生成代码,而非手写 struct。

















