# 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)
- 重要更新说明
- linechain发布
- linechain新版设计
- 引言一
- 引言二
- 引言三
- vs-code设置及开发环境设置
- BoltDB数据库应用
- 关于Go语言、VS-code的一些Tips
- 区块链的架构
- 网络通信与区块链
- 单元测试
- 比特币脚本语言
- 关于区块链的一些概念
- 区块链组件
- 区块链第一版:基本原型
- 区块链第二版:增加工作量证明
- 区块链第三版:持久化
- 区块链第四版:交易
- 区块链第五版:实现钱包
- 区块链第六版:实现UTXO集
- 区块链第七版:网络
- 阶段小结
- 区块链第八版:P2P
- P2P网络架构
- 区块链网络层
- P2P区块链最简体验
- libp2p建立P2P网络的关键概念
- 区块链结构层设计与实现
- 用户交互层设计与实现
- 网络层设计与实现
- 建立节点发现机制
- 向区块链网络请求区块信息
- 向区块链网络发布消息
- 运行区块链
- LineChain
- 系统运行流程
- Multihash
- 区块链网络的节点发现机制深入探讨
- DHT
- Bootstrap
- 连接到所有引导节点
- Advertise
- 搜索其它peers
- 连接到搜到的其它peers
- 区块链网络的消息订发布-订阅机制深入探讨
- LineChain:适用于智能合约编程的脚本语言支持
- LineChain:解决分叉问题
- LineChain:多重签名
- libp2p升级到v0.22版本
- 以太坊基础
- 重温以太坊的树结构
- 世界状态树
- (智能合约)账户存储树
- 交易树
- 交易收据树
- 小结
- 以太坊的存储结构
- 以太坊状态数据库
- MPT
- 以太坊POW共识算法
- 智能合约存储
- Polygon Edge
- block结构
- transaction数据结构
- 数据结构小结
- 关于本区块链的一些说明
- UML工具-PlantUML
- libp2p介绍
- JSON-RPC
- docker制作:启动多个应用系统
- Dockerfile
- docker-entrypoint.sh
- supervisord.conf
- docker run
- nginx.conf
- docker基础操作整理
- jupyter计算交互环境
- git技巧一
- git技巧二
- 使用github项目的最佳实践
- windows下package管理工具