# 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://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管理工具