
随着LLM引用的大火,json-rpc 2.0协议应用越来越广泛,它使用JSON (JavaScript Object Notation) 来编码请求和响应。JSON-RPC 目前有两个主要版本:1.0 和 2.0。2.0 版本在1.0的基础上进行了改进,包括更严格的错误处理和更清晰的规范定义。具体协议内容如下:
JSON-RPC 请求是一个JSON对象,包含以下字段:
jsonrpc: 必须为字符串"2.0",表示协议版本。
method: 必须为字符串,表示要调用的远程过程名。
params: 可选字段,可以是数组或对象,包含方法调用的参数。
id: 可选字段,用于标识请求,如果存在,响应中也必须包含相同的id。JSON-RPC 响应也是一个JSON对象,包含以下字段:
jsonrpc: 必须为字符串"2.0"。
result: 可选字段,包含方法调用的结果。
error: 可选字段,如果方法调用失败,包含错误信息。
id: 必须与请求中的id相同。SON-RCP错误处理:JSON-RPC 错误响应包含一个error字段,该字段是一个对象,包含以下字段:
code: 必须为整数,表示错误代码。
message: 必须为字符串,表示错误信息。
data: 可选字段,包含错误的详细数据。错误代码
JSON-RPC 定义了一些标准的错误代码:
-32700: 解析错误,JSON 解析失败。
-32600: 无效的请求,请求格式不正确。
-32601: 方法未找到,请求的方法不存在。
-32602: 无效的参数,参数格式或值不正确。
-32603: 内部错误,服务器内部错误。
-32000 到 -32099: 服务器错误,服务器定义的错误。JSON-RPC通常通过HTTP协议进行通信,但也可以使用其他传输协议,如WebSockets。
其实我们按照json-rpc协议的规范来进行传参数和返回值,并使用json格式进行序列化传输,就可以实现json-rpc协议,作为gopher我们不需要自己实现,net/rpc/jsonrpc已经包含了完整的实现。
下面我们通过一个例子分析客户端的源码实现
package main
import (
"log"
"net"
"net/rpc/jsonrpc"
"time"
"learn/vscode/jsonrpc/exp1/model"
)
func main() {
conn, err := net.DialTimeout("tcp", "127.0.0.1:8080", 30*time.Second) // 30秒超时时间
if err != nil {
log.Fatalf("客户端发起连接失败:%v", err)
}
defer conn.Close()
client := jsonrpc.NewClient(conn)
var item model.Item
client.Call("ServiceHandler.GetName", 1, &item)
log.Printf("ServiceHandler.GetName 返回结果:%v\n", item)
var resp model.Response
item = model.Item{2, "学院君2"}
client.Call("ServiceHandler.SaveName", item, &resp)
log.Printf("ServiceHandler.SaveName 返回结果:%v\n", resp)
}运行结果如下
2025/06/21 22:38:12 ServiceHandler.GetName 返回结果:{1 学院君}
2025/06/21 22:38:12 ServiceHandler.SaveName 返回结果:{true 2 存储成功}可以看到除了 jsonrpc.NewClient和Call方法外基本和普通的httpclient差不多,下面看下NewClient的具体实现,源码位于net/rpc/jsonrpc/client.go
func NewClient(conn io.ReadWriteCloser) *rpc.Client {
return rpc.NewClientWithCodec(NewClientCodec(conn))
}没错,它调用了上一篇介绍的net/rpc包的NewClientWithCodec 方法。只不过参数里的Client的编码解码方式换成了json
func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
return &clientCodec{
dec: json.NewDecoder(conn),
enc: json.NewEncoder(conn),
c: conn,
pending: make(map[uint64]string),
}
}然后是Call方法,没错直接使用了net/rpc的Call方法
func (client *Client) Call(serviceMethod string, args any, reply any) error {
call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error
}可以看到,除了编码解码方式完,其他流程和gob编码方式的rpc几乎一样。
本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!