Go标准库net/rpc/jsonrpc不支持原生异步调用,因其*jsonrpc.Client仅封装net/rpc.Client并替换编解码器,未暴露Go方法,底层仍为同步调度;需手动用goroutine+channel封装Call实现“类异步”。

Go 标准库 net/rpc/jsonrpc 不支持原生异步调用 —— 它只提供同步阻塞的 Call,所谓“异步”必须由你手动封装 goroutine + channel 实现。
为什么 jsonrpc.Client 没有 Go 方法?
标准库 net/rpc/jsonrpc 的 *jsonrpc.Client 是对 net/rpc.Client 的封装,但只实现了 Call,没暴露 Go 方法。这是因为 jsonrpc 包本身不重写 client 行为,它只替换编解码器(ClientCodec),底层仍复用 net/rpc.Client 的同步调度逻辑。
常见错误现象:
直接调用 client.Go(...) 会报错 undefined: client.Go 或 panic:interface conversion: rpc.ClientCodec is *jsonrpc.clientCodec, not *rpc.jsonClientCodec
-
net/rpc.Client的Go方法依赖内部clientCodec实现细节,而jsonrpc.NewClient返回的 client 使用的是私有jsonrpc.clientCodec,类型不匹配 - 即使强行类型断言,也会因字段结构差异导致 panic(如缺少
send字段) - 别试图 patch
jsonrpc源码 —— 它不是设计来支持异步的
如何安全实现“类异步”调用?
核心思路:用 goroutine 包一层 Call,把结果和 error 通过 channel 传出。这不是真正的 RPC 异步(底层仍是同步 TCP 请求),但能避免阻塞主流程。
立即学习“go语言免费学习笔记(深入)”;
示例代码(精简可直接复用):
type AsyncResult struct {
Reply interface{}
Err error
}
<p>func AsyncCall(client *rpc.Client, method string, args interface{}, reply interface{}) <-chan AsyncResult {
ch := make(chan AsyncResult, 1)
go func() {
err := client.Call(method, args, reply)
ch <- AsyncResult{Reply: reply, Err: err}
}()
return ch
}使用方式:
ch := AsyncCall(myJSONRPCClient, "Arith.Add", &Args{7, 3}, &Reply{})
result := <-ch
if result.Err != nil {
log.Printf("call failed: %v", result.Err)
} else {
log.Printf("got reply: %+v", result.Reply)
}- 务必给 channel 加 buffer(如
make(chan AsyncResult, 1)),否则 goroutine 可能永久阻塞在 send 上 -
reply必须是指针(如&Reply{}),且字段需导出 + 有json:tag(例如Sum int `json:"sum"`) - 不要在 goroutine 内部直接修改外部变量 ——
reply是传入的指针,修改是安全的,但别把它当成返回值再赋给别的变量
跨语言场景下更要小心序列化兼容性
当你用 Go 的 jsonrpc 对接 Python/Java 客户端时,“异步封装”本身没问题,但序列化层容易翻车:
- Go 端方法签名必须严格满足
func(*T, *S) error,且T和S所有字段都要有json:tag,否则 Java/Python 解析params或result会丢字段 - JSON-RPC 2.0 要求
id字段必须存在且类型一致(Go 默认用int,Python 客户端可能发string),不匹配会导致响应被丢弃 - 错误格式必须是
{"error": {"code": -32603, "message": "..."}},Go 的jsonrpc默认只返回error.Error()字符串,不带 code —— 需手动包装成struct{ Code int; Message string }并返回非 nil error
真正要高并发异步调用,别硬扛 net/rpc/jsonrpc;它本质是玩具级协议,连接无复用、无超时控制、无重试。复杂服务请换 gRPC 或基于 http.Client 自建 JSON-RPC 2.0 client —— 后者才能自然支持 context、timeout、cancel 和真正的并发请求。


















