Go 语言 net/rpc 实现双向 RPC 需双方均启动 RPC Server 并互相注册 handler;客户端须监听地址、导出方法、告知服务端 callback 地址,服务端通过新建连接调用;推荐 JSON-RPC + HTTP,注意连接生命周期与资源泄漏防控。

Go 语言标准库的 net/rpc 包原生支持双向 RPC,但需注意:它本身不直接提供“客户端也能被服务端调用”的能力,必须通过双方都启动 RPC 服务并互相注册 handler 来实现双向调用。核心思路是:客户端既是 RPC 调用方(client),也是 RPC 服务端(server);服务端同理。
服务端主动调用客户端的关键:客户端也要启动 RPC Server
标准 rpc.ServeConn 或 rpc.ServeHTTP 只监听连接,不自动反向暴露接口。要让服务端能调用客户端方法,客户端必须:
- 监听一个本地或可访问的网络地址(如
localhost:8081),启动自己的rpc.Server - 注册至少一个 handler(例如
ClientHandler),导出方法(首字母大写 + 符合 RPC 方法签名:func(*Args, *Reply) error) - 在连接建立后,把自身服务地址告诉服务端(可通过初始握手消息、配置文件、或服务端预置方式)
使用 HTTP+JSON-RPC 实现双向通信更简洁
net/rpc/jsonrpc 比默认 gob 更易调试,且天然兼容 HTTP。推荐结构:
- 服务端监听
:8080,提供UserSvc和NotifySvc等服务 - 客户端启动时监听
:8081,注册ClientCallback服务(含OnDataUpdate等方法) - 客户端首次连接服务端时,发送自身 callback 地址(如
http://127.0.0.1:8081/rpc),服务端保存并创建对应 client - 服务端后续可通过
jsonrpc.NewClient连接该地址,发起回调
避免连接循环与资源泄漏的实用技巧
双向连接容易引发 goroutine 泄漏或死锁,务必注意:
立即学习“go语言免费学习笔记(深入)”;
- 每个 RPC 连接应有明确生命周期,建议用
context.WithTimeout控制调用超时 - 客户端连接服务端后,不要复用同一连接发起反向调用(gob 编码不支持双工);应新建独立连接到客户端监听地址
- 服务端保存客户端 callback client 时,用
sync.Map并配合心跳/重连机制;客户端退出前主动通知服务端注销 - 所有 handler 方法末尾加
defer日志或计数器,便于排查未结束调用
一个最小可运行示例的核心片段
客户端启动 RPC server:
l, _ := net.Listen("tcp", ":8081")
s := rpc.NewServer()
s.RegisterName("Client", &ClientHandler{})
go http.Serve(l, s)
服务端调用客户端:
client, _ := jsonrpc.DialHTTP("tcp", "127.0.0.1:8081")
var reply string
client.Call("Client.OnDataUpdate", args, &reply)
关键点:方法名格式为 "ServiceName.MethodName",参数和返回值必须是导出类型,且有无参构造函数。
不复杂但容易忽略:双向 RPC 不是开箱即用的“对等通信”,而是两个独立 RPC 系统通过约定地址和协议协作完成的。只要双方都跑 server、都建 client、地址互通、方法匹配,就能稳定互调。










