rpc.register必须传指针且方法签名严格限定,因其依赖硬编码规则识别可导出方法:首字母大写、恰好两个参数(第一个可序列化类型、第二个为*t指针)、返回error;否则反射时跳过该方法,导致客户端调用报“method not found”或gob解码失败。

Go 微服务里所谓“透明调用远程函数”,本质是 RPC 框架在帮你做序列化、网络传输、反序列化、方法分发四件事;不理解这四步,就容易把 rpc.Register 当成魔法,一出错就卡在 connection refused 或 invalid method 上。
为什么 rpc.Register 必须传指针且方法要满足特定签名
Go 官方 net/rpc 不是泛型反射框架,它靠硬编码规则识别可导出方法:必须是公开方法(首字母大写),参数恰好两个,第一个是输入值(可序列化类型),第二个是输出指针(*T),返回值只能是 error。这不是限制,而是为了在运行时能无歧义地解析调用帧。
-
rpc.Register实际遍历结构体所有方法,只保留符合该签名的——漏掉*或多一个参数,该方法就彻底不可见 - 如果传入的是值类型(如
func(*Args, *int) error中第一个参数写成Args),gob编码会失败,但错误常被吞掉,只表现为客户端Call返回EOF或空响应 - 结构体字段也必须导出(首字母大写),否则
gob无法序列化,服务端收到零值
rpc.ServeConn 和 rpc.HandleHTTP 的底层差异
两者都处理连接,但协议栈完全不同:rpc.ServeConn 直接在 TCP 连接上跑原生 RPC 帧(含消息头、长度、方法名、参数 blob);rpc.HandleHTTP 则把 RPC 请求包装成 HTTP POST,路径固定为 /RPC2,Body 是 gob 编码数据。前者轻量、无 HTTP 开销;后者能穿代理、走 HTTPS,但多一层封装和校验。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
rpc.HandleHTTP后必须配http.Serve,不能只监听 TCP —— 否则请求根本进不来 -
rpc.ServeConn需手动 accept 并起 goroutine,否则阻塞;而http.Serve内部已做并发处理 - HTTP 方式下,客户端仍要用
rpc.DialHTTP,不是http.Get;否则服务端收不到合法 RPC 帧
跨语言调用为什么 net/rpc 行不通,而 jsonrpc 可以
net/rpc 默认用 encoding/gob,这是 Go 专属二进制格式,其他语言没有标准实现;net/rpc/jsonrpc 则把请求/响应全转成 JSON 对象,只要对方能解析 JSON 并按 RPC 协议约定拼字段,就能通信。
-
jsonrpc仍基于 TCP,不是 HTTP —— 它只是用 JSON 替代 gob,协议头还是自定义的,不兼容标准 JSON-RPC 2.0 规范 - 即便用
jsonrpc,方法签名约束不变:第二个参数仍必须是指针,否则解码后无法写回 - 真正跨语言生产环境,基本不用官方
jsonrpc,而是切到gRPC或Thrift,因为它们有 IDL + 多语言代码生成,而非手写适配逻辑
客户端 conn.Call 调用失败时最该先查什么
90% 的 “调用失败” 其实卡在连接建立或帧解析阶段,而不是业务逻辑出错。别急着改方法体,先确认三件事:
- 服务端是否真在监听?用
telnet localhost 1234测试端口通不通,不通就检查net.Listen是否成功、防火墙是否放行 - 服务名和方法名拼写是否完全匹配?
conn.Call("Arith.Multiply", ...)中Arith是rpc.RegisterName传入的名字,不是结构体名;大小写、点号缺一不可 - 客户端传参类型是否和服务端期望一致?比如服务端定义
type Args struct{ A, B int },客户端却传map[string]int,gob解码直接 panic,连接会被服务端关闭
RPC 不是黑盒,每一层(序列化、传输、分发)都有明确职责;一旦出问题,顺着这四步倒查,比重写整个服务更快。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










