💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# JSON-RPC 概念 JSON-RPC,是一个无状态且轻量级的远程过程调用(RPC)传送协议,其传递内容透过[JSON](https://zh.wikipedia.org/wiki/JSON "JSON")为主。相较于一般的[REST](https://zh.wikipedia.org/wiki/REST "REST")透过网址(如`GET /user`)调用远程服务器,JSON-RPC 直接在内容中定义了欲调用的函数名称(如`{"method": "getUser"}`),这也令开发者不会陷于该使用 PUT 或者 PATCH 的问题之中。 本规范主要定义了一些数据结构及其相关的处理规则。它允许运行在基于[Socket](https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E6%8F%92%E5%BA%A7)、[HTTP](https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE "超文本传输协议")等诸多不同消息传输环境的同一进程中。其使用[JSON](https://zh.wikipedia.org/wiki/JSON "JSON")(RFC 4627)作为数据格式。(据维基百科) # 约定 由于 JSON-RPC 使用[JSON](https://zh.wikipedia.org/wiki/JSON "JSON"),它具有与其相同的类型系统。[JSON](https://zh.wikipedia.org/wiki/JSON "JSON")可以表示四个基本类型(String、Numbers、Boolean 和 Null)和两个结构化类型(Objects 和 Array)。任何时候文档涉及[JSON](https://zh.wikipedia.org/wiki/JSON "JSON")数据类型,第一个字母都必须大写:Object、Array、String、Number、Boolean、Null。包括 True 和 False 也要大写。 在客户端与任何被匹配到的服务端之间交换的所有成员名字应是区分大小写的。 函数、方法、过程都可以认为是可以互换的。客户端被定义为请求对象的来源及响应对象的处理程序。服务端被定义为响应对象的起源和请求对象的处理程序。 # 兼容性 JSON-RPC 2.0 的请求对象和响应对象可能无法在较旧的 JSON-RPC 1.0 客户端或服务端正常运行,然而我们可以很容易在两个版本间区分出 2.0。因为 2.0 版本中必须夹带一个命名为 jsonrpc 且值为 2.0 的字段。而 1.0 版本是没有此字段的。大部分的 2.0 实现应该考虑尝试兼容或者处理 1.0 的对象,即使不是对等的也应给其相关提示。 # 请求对象 发送一个请求对象至服务端代表一个[RPC](https://zh.wikipedia.org/wiki/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8 "远程过程调用")调用,一个请求对象包含下列成员: * jsonrpc * method * params * id id成员:如果包含在响应对象,服务端必须回答相同的值。这个成员用来两个对象之间的关联上下文。在请求对象中不建议使用 NULL 作为 id 值,因为该规范将使用空值认定为未知id的请求。另外,由于JSON-RPC 1.0 的通知使用了空值,这可能引起处理上的混淆。使用小数是不确定性的,因为许多十进制小数不能精准的表达为二进制小数。 # 通知 没有包含 id 成员的请求对象为通知, 作为通知的请求对象表明客户端对相应的响应对象并不感兴趣,本身也没有响应对象需要返回给客户端。 # 参数结构 [RPC](https://zh.wikipedia.org/wiki/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8 "远程过程调用")调用如果存在参数则必须为基本类型或结构化类型的参数值,要么为索引数组,要么为关联数组对象。 * 索引:参数必须为数组,并包含与服务端预期顺序一致的参数值。 * 关联名称:参数必须为对象,并包含与服务端相匹配的参数成员名称。没有在预期中的成员名称可能会引起错误。名称必须完全匹配,包括方法的预期参数名以及大小写。 # 响应对象 当发起一个[RPC](https://zh.wikipedia.org/wiki/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8 "远程过程调用")调用时,除通知之外,服务端都必须回复响应。响应表示为一个 JSON 对象,使用以下字段: * jsonrpc * result * error * id 响应对象必须包含 result 或 error 字段,但两个字段不能同时存在。 # 错误对象 当一个[RPC](https://zh.wikipedia.org/wiki/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8 "远程过程调用")调用遇到错误时,返回的响应对象必须包含错误成员参数,并且为带有下列成员参数的对象: * code * message * data \-32768 至 -32000 为保留的预定义错误代码。在该范围内的错误代码不能被开发者自己定义,保留下列以供将来使用。错误代码基本与[XML-RPC](https://zh.wikipedia.org/wiki/XML-RPC "XML-RPC")[建议](http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)的一样 | 错误码 | 消息 | 解释 | | --- | --- | --- | | \-32700 | Parse error - 语法解析错误 | 服务端接收到无效的 JSON。该错误发送于服务器尝试解析 JSON 文本 | | \-32600 | Invalid Request - 无效请求 | 发送的 JSON 内容不是一个有效的请求对象。 | | \-32601 | Method not found - 找不到方法 | 该方法不存在或无效。 | | \-32602 | Invalid params - 无效的参数 | 无效的方法参数。 | | \-32603 | Internal error - 内部错误 | JSON-RPC 内部错误。 | | \-32000 to -32099 | Server error - 服务端错误 | 预留用于自定义的服务器错误。 | 除此之外剩余的错误类型代码可供应用程序作为自定义错误。 # 批量调用 当需要同时发送多个请求对象时,客户端可以发送一个包含所有请求对象的数组。 当批量调用的所有请求对象处理完成时,服务端则需要返回一个包含相对应的响应对象数组。每个响应对象都应对应每个请求对象,除非是通知的请求对象。服务端可以并发的,以任意顺序和任意宽度的并行性来处理这些批量调用。 这些相应的响应对象可以任意顺序的包含在返回的数组中,而客户端应该是基于各个响应对象中的 id 成员来匹配对应的请求对象。 若批量调用的 RPC 操作本身非一个有效[JSON](https://zh.wikipedia.org/wiki/JSON "JSON")或一个至少包含一个值的数组,则服务端返回的将单单是一个响应对象而非数组。若批量调用没有需要返回的响应对象,则服务端不需要返回任何结果且不能返回一个空数组给客户端。 # Go JSON-RPC Go 标准包中已经提供了对 RPC 的支持,而且支持三个级别的 RPC:TCP、HTTP、JSONRPC。依托JSONRPC这个框架实现的RPC服务只能被Go语言调用,因为其在内部采用了Gob编码。 Go RPC 的函数只有符合下面的条件才能被远程访问: * 函数必须是导出的(首字母大写) * 必须有两个参数,并且是导出类型或者内建类型 * 第二个参数必须是指针类型的 * 函数还要有一个返回值 error ## JSON RPC server 使用 gorilla rpc/json 使用 Gorilla 工具包来简化默认的 `net/rpc/jsonrpc`. Gorilla 工具包与默认包之间的微小差别在于它需要方法签名来接受 `* Request` 对象作为第一个参数,并将 Args 参数更改为指针 `* Args`。 ## JSON RPC client 使用 gorilla rpc/json ``` ~~~go package main import ( "bytes" "github.com/gorilla/rpc/json" "log" "net/http" . "rpc-golang/json-rpc" ) func checkError(err error) { if err != nil { log.Fatalf("%s", err) } } func main() { url := "http://localhost:1234/rpc" args := Args{ A: 2, B: 3, } message, err := json.EncodeClientRequest("Arith.Multiply", args) checkError(err) resp, err := http.Post(url, "application/json", bytes.NewReader(message)) defer resp.Body.Close() checkError(err) reply := new(int) err = json.DecodeClientResponse(resp.Body, reply) checkError(err) log.Printf("%d * %d = %d\n", args.A, args.B, *reply) } ~~~ ``` ## JSON RPC client 使用 Python ``` ~~~python # -*- coding: utf-8 -*- import requests """ JSON RPC client using Python """ def rpc_call(): url = 'http://localhost:1234/rpc' payload = { 'id': 1, 'method': 'Arith.Multiply', 'params': [{'A': 2, 'B': 3}] } r = requests.post(url, json=payload) print r.text if __name__ == '__main__': rpc_call() ~~~ ``` ## 目录结构 ![](https://img.kancloud.cn/e2/44/e2448e208f3e1d0fa1966baa0ca05878_280x278.png) 代码参考地址:[https://github.com/happy-python/rpc-golang](https://github.com/happy-python/rpc-golang)