1. 如何使用 Etherscan 的 API
雖然以太坊提供了 Web3 和 Json Rpc 這 2 種 API,geth 也額外提供了一些 API ,但是對於開發以太坊應用來說還是顯得有些不足,比如說獲取交易記錄的時間,需要先通過交易的 hash 找到該交易對應的區塊 id,然後才能找到對應的時間,查詢起來相當不方便。
好在 Etherscan 對外提供了一些公共的 API,給我們提供了額外的能力來處理更多的業務場景。
為了方便開發人員更好地使用 ethersacn.io ,網站提供了 一系列 API 供開發人員使用。
API 的使用非常簡單,基本上都是 get 方法,通過 http 請求就可以直接調用,在每個 Api 的說明文檔都有對應的例子可以查看。
API 主要包含以下模塊:賬號、智能合約、交易、區塊、事件日誌、代幣及工具等。
賬號相關的 API,有獲取賬號金額,獲取交易記錄等,該模塊提供的 API 最多。
API 示例
https://api.etherscan.io/api?mole)=account&action=balance&address=&tag=latest&apikey=YourApiKeyToken
參數說明
其中 mole、action、apikey 是每個 API 都有的參數,其他的參數則因不同 API 而不同。
返回結果
API 示例
https://api.etherscan.io/api?mole=account&action=balancemulti&address=,,&tag=latest&apikey=YourApiKeyToken
參數說明
(前面有講過的參數就不講了,下同)
與單個賬號金額 API 相比,參數 address 用 , 號分隔多個賬號,最多可支持 20 個賬號的金額查詢。
返回結果
API 示例
https://api.etherscan.io/api?mole=account&action=txlist&address=&startblock=0&endblock=99999999&page=1&offset=10&sort=asc&apikey=YourApiKeyToken
參數說明
返回結果
API 示例
https://api.etherscan.io/api?mole=account&action=txlistinternal&address=&startblock=0&endblock=2702578&page=1&offset=10&sort=asc&apikey=YourApiKeyToken
參數說明
參數與上一個 API 基本相同,只有 action 是 txlistinternal 這一點不同,這 2 種交易的區別是什麼呢?簡單的理解就是「正常」的交易是會記錄到區塊鏈上的,而「內部」交易是指不會記錄到區塊鏈上的記錄,比如交易失敗的記錄。
另外這個 API 還可以通過交易 hash 查看交易的詳情。
https://api.etherscan.io/api?mole=account&action=txlistinternal&txhash=&apikey=YourApiKeyToken
返回結果
API 示例
參數說明
返回結果
API 示例
參數說明
返回結果
智能合約相關的 API,其實只有一個獲取智能合約介面的 API,但是這個 API 非常有用。
API 示例
參數說明
智能合約的 abi 就是一個 json 對象,通過這個對象我們可以調用其介面方法,後面會寫一篇文章介紹如何操作 abi 對象,敬請期待。
返回結果
返回結果內容比較長,這里省略,就是一個 json 對象,感興趣的可以自行調用該 API 看結果。
賬號和智能合約的 API 已經能滿足大部分的業務需求了,其他模塊的 API 感覺沒什麼太大的作用,這里就不介紹了,感興趣的讀者可以自行查閱。
這里再說下 API 的使用限制,剛才提到每個 API 都有一個 apikey 參數,如果 API 沒加上這個參數的話,每個 API 的請求次數不能超過 5 次每秒。
Etherscan 提供的這些 API 有些是和以太坊提供的 API 有重復的,比如說獲取賬號金額,獲取事件日誌記錄等,但有一些 API 給我們帶來了很大的便利性,比如獲取賬號交易記錄,有了這個 API 就不用使用幾個原生 API 進行各種數據拼接了。
另外 Etherscan 的這套 API 在 Rinkeby 測試網路也有一套一模一樣的,區別只是前面的 url 不同,Rinkeby 的是: api-rinkeby.etherscan.io ,感興趣的同學可以去試試。
2. json-rpc璋冪敤閿欒寮傚父錛孭arse error,閿欒鐮侊細-32700
閿欒璇鋒眰濡備笅錛
姝g『浼犲弬鏂瑰紡鏄灝唈son鍙樻垚瀛楃︿覆浼犵粰data
3. DApp開發入門
本文僅介紹以太坊系列的DApp開發,其他鏈原理差不太多。
MetaMask安裝完成並運行後,可以在Chrome控制台列印 MetaMask注入的window.ethereum對象
關於ethereum對象,我們只需要關心 ethereum.request 就足夠了,MetaMask 使用 ethereum.request(args) 方法 來包裝 RPC API。這些 API 基於所有以太坊客戶端公開的介面。 簡單來說錢包交互的大部分操作都是由 request() 方法實現,通過傳入不同的方法名來區分。
⚠️ 即使ethereum對象中提供了chainId,isMetaMask,selectAddress屬性,我們也不能完全相信這些屬性,他們是不穩定或不標准,不建議使用。我們可以通過上面說的request方法,拿到可靠的數據 。
錢包通過method方法名,進行對應的實現 以獲取錢包地址為例
調用 ethereum.request({ method: "eth_requestAccounts" }) ,錢包實現了該方法,那麼就可以拿到錢包的地址了。
MetaMask注入的 window.ethereum 就是一個Provider,一個RPC節點也是一個Provider,通過Provider,我們有了訪問區塊鏈的能力。 在連接到錢包的情況下,通常使用錢包的Provider就可以了, ethers.providers.Web3Provider(ethereum)
如果只需要查詢一些區塊鏈數據,可以使用EtherscanProvider 和 InfuraProvider 連接公開的 第三方節點服務提供商 。JsonRpcProvider 和 IpcProvider 允許連接到我們控制或可以訪問的以太坊節點。
獲取當前賬戶余額
獲取最新區塊號
其他RPC操作,可以通過 JSON-RPC 查看。
通過 ethers.js 可以連接ERC20的合約,合約編譯後會生成ABI,合約部署後,會生成合約地址,開發者通過 ABI和合約地址 ,對合約發送消息。
合約中的方法大致分為兩種: 視圖方法(免費),非視圖方法(消耗Gas) ,可以通過ABI查看方法類型。
⚠️ ERC20需要多加關注的是 Approve() 方法以及 transfer() 和 transferFrom() 的區別 ,授權過的代幣,被授權的那一方,可以通過調用 transferFrom() 方法,轉走你授權數量內的代幣,所以授權是一個很危險的操作,假設你授權了一個不良的合約,那你會面臨授權的token被轉走的風險,即使你沒有泄露私鑰助記詞。
便利三方庫: web3-react use-wallet
文檔: doc.metamask.io ethers
4. 以太坊stratum協議原理
參照比特幣的 stratum協議 和 NiceHash的stratum協議規范 編寫了一版以太坊版本的stratum協議說明.
stratum協議是目前最常用的礦機和礦池之間的TCP通訊協議。
以太坊是一個去中心化的網路架構,通過安裝Mist客戶端的節點來轉發新交易和新區塊。而礦機、礦池也同時形成了另一個網路,我們稱之為礦工網路。
礦工網路分成礦機、礦池、錢包等幾個主要部分,有時礦池軟體與錢包安裝在一起,可合稱為礦池。
礦機與礦池軟體之間的通訊協議是 stratum ,而礦池軟體與錢包之間的通訊是 bitcoinrpc 介面。
stratum是 JSON 為數據格式.
礦機啟動,首先以 mining.subscribe 方法向礦池連接,用來訂閱工作。
礦池以 mining.notify 返回訂閱號、ExtraNonce1和ExtraNonce2_size。
Client:
Server:
其中:
是 訂閱號 ;
080c是 extranonce ,Extranonce可能最大3位元組;
礦機以 mining.authorize 方法,用某個帳號和密碼登錄到礦池,密碼可空,礦池返回 true 登錄成功。該方法必須是在初始化連接之後馬上進行,否則礦機得不到礦池任務。
Client:
Server:
難度調整由礦池下發給礦機,以 mining.set_difficulty 方法調整難度, params 中是難度值。
Server:
礦機會在下一個任務時採用新難度,礦池有時會馬上下發一個新任務並且把清理任務設為true,以便礦機馬上以新難度工作。
該命令由礦池定期發給礦機,當礦機以 mining.subscribe 方法登記後,礦池應該馬上以 mining.notify 返回該任務。
Server:
任務ID : bf0488aa ;
seedhash : 。每一個任務都發送一個seedhash來支持盡可能多的礦池,這可能會很快地在貨幣之間交換。
headerhash : 。
boolean cleanjobs : true 。如果設為true,那麼礦工需要清理任務隊列,並立即開始從事新提供的任務,因為所有舊的任務分享都將導致陳舊的分享錯誤。如果是 false 則等當前任務結束才開始新任務。
礦工使用seedhash識別DAG,然後帶著headerhash,extranonce和自己的minernonce尋找低於目標的share(這是由提供的難度而產生的)。
礦機找到合法share時,就以」 mining.submit 「方法向礦池提交任務。礦池返回true即提交成功,如果失敗則error中有具體原因。
Client:
任務ID : bf0488aa
minernonce : 6a909d9bbc0f 。注意minernonce是6個位元組,因為提供的extranonce是2個位元組。如果礦池提供3位元組的extranonce,那麼minernonce必須是5位元組
Server:
一般的礦機與礦池通訊過程就如下所示:
5. 以太坊多節點私有鏈部署
假設兩台電腦A和B
要求:
1、兩台電腦要在一個網路中,能ping通
2、兩個節點使用相同的創世區塊文件
3、禁用ipc;同時使用參數--nodiscover
4、networkid要相同,埠號可以不同
1.4 搭建私有鏈
1.4.1 創建目錄和genesis.json文件
創建私有鏈根目錄./testnet
創建數據存儲目錄./testnet/data0
創建創世區塊配置文件./testnet/genesis.json
1.4.2 初始化操作
cd ./eth_test
geth --datadir data0 init genesis.json
1.4.3 啟動私有節點
1.4.4 創建賬號
personal.newAccount()
1.4.5 查看賬號
eth.accounts
1.4.6 查看賬號余額
eth.getBalance(eth.accounts[0])
1.4.7 啟動&停止挖礦
啟動挖礦:
miner.start(1)
其中 start 的參數表示挖礦使用的線程數。第一次啟動挖礦會先生成挖礦所需的 DAG 文件,這個過程有點慢,等進度達到 100% 後,就會開始挖礦,此時屏幕會被挖礦信息刷屏。
停止挖礦,在 console 中輸入:
miner.stop()
挖到一個區塊會獎勵5個以太幣,挖礦所得的獎勵會進入礦工的賬戶,這個賬戶叫做 coinbase,默認情況下 coinbase 是本地賬戶中的第一個賬戶,可以通過 miner.setEtherbase() 將其他賬戶設置成 coinbase。
1.4.8 轉賬
目前,賬戶 0 已經挖到了 3 個塊的獎勵,賬戶 1 的余額還是0:
我們要從賬戶 0 向賬戶 1 轉賬,所以要先解鎖賬戶 0,才能發起交易:
發送交易,賬戶 0 -> 賬戶 1:
需要輸入密碼 123456
此時如果沒有挖礦,用 txpool.status 命令可以看到本地交易池中有一個待確認的交易,可以使用 eth.getBlock("pending", true).transactions 查看當前待確認交易。
使用 miner.start() 命令開始挖礦:
miner.start(1);admin.sleepBlocks(1);miner.stop();
新區塊挖出後,挖礦結束,查看賬戶 1 的余額,已經收到了賬戶 0 的以太幣:
web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
用同樣的genesis.json初始化操作
cd ./eth_test
geth --datadir data1 init genesis.json
啟動私有節點一,修改 rpcport 和port
可以通過 admin.addPeer() 方法連接到其他節點,兩個節點要要指定相同的 chainID。
假設有兩個節點:節點一和節點二,chainID 都是 1024,通過下面的步驟就可以從節點二連接到節點一。
首先要知道節點一的 enode 信息,在節點一的 JavaScript console 中執行下面的命令查看 enode 信息:
admin.nodeInfo.enode
" enode://@[::]:30303 "
然後在節點二的 JavaScript console 中執行 admin.addPeer(),就可以連接到節點一:
addPeer() 的參數就是節點一的 enode 信息,注意要把 enode 中的 [::] 替換成節點一的 IP 地址。連接成功後,節點一就會開始同步節點二的區塊,同步完成後,任意一個節點開始挖礦,另一個節點會自動同步區塊,向任意一個節點發送交易,另一個節點也會收到該筆交易。
通過 admin.peers 可以查看連接到的其他節點信息,通過 net.peerCount 可以查看已連接到的節點數量。
除了上面的方法,也可以在啟動節點的時候指定 --bootnodes 選項連接到其他節點。 bootnode 是一個輕量級的引導節點,方便聯盟鏈的搭建 下一節講 通過 bootnode 自動找到節點
參考: https://cloud.tencent.com/developer/article/1332424
6. 一學就會,手把手教你用Go語言調用智能合約
智能合約調用是實現一個 DApp 的關鍵,一個完整的 DApp 包括前端、後端、智能合約及區塊 鏈系統,智能合約的調用是連接區塊鏈與前後端的關鍵。
我們先來了解一下智能合約調用的基礎原理。智能合約運行在以太坊節點的 EVM 中。因此要 想調用合約必須要訪問某個節點。
以後端程序為例,後端服務若想連接節點有兩種可能,一種是雙 方在同一主機,此時後端連接節點可以採用 本地 IPC(Inter-Process Communication,進 程間通信)機制,也可以採用 RPC(Remote Procere Call,遠程過程調用)機制;另 一種情況是雙方不在同一台主機,此時只能採用 RPC 機制進行通信。
提到 RPC, 讀者應該對 Geth 啟動參數有點印象,Geth 啟動時可以選擇開啟 RPC 服務,對應的 默認服務埠是 8545。。
接著,我們來了解一下智能合約運行的過程。
智能合約的運行過程是後端服務連接某節點,將 智能合約的調用(交易)發送給節點,節點在驗證了交易的合法性後進行全網廣播,被礦工打包到 區塊中代表此交易得到確認,至此交易才算完成。
就像資料庫一樣,每個區塊鏈平台都會提供主流 開發語言的 SDK(Software Development Kit,軟體開發工具包),由於 Geth 本身就是用 Go 語言 編寫的,因此若想使用 Go 語言連接節點、發交易,直接在工程內導入 go-ethereum(Geth 源碼) 包就可以了,剩下的問題就是流程和 API 的事情了。
總結一下,智能合約被調用的兩個關鍵點是節點和 SDK。
由於 IPC 要求後端與節點必須在同一主機,所以很多時候開發者都會採用 RPC 模式。除了 RPC,以太坊也為開發者提供了 json- rpc 介面,本文就不展開討論了。
接下來介紹如何使用 Go 語言,藉助 go-ethereum 源碼庫來實現智能合約的調用。這是有固定 步驟的,我們先來說一下總體步驟,以下面的合約為例。
步驟 01:編譯合約,獲取合約 ABI(Application Binary Interface,應用二進制介面)。 單擊【ABI】按鈕拷貝合約 ABI 信息,將其粘貼到文件 calldemo.abi 中(可使用 Go 語言IDE 創建該文件,文件名可自定義,後綴最好使用 abi)。
最好能將 calldemo.abi 單獨保存在一個目錄下,輸入「ls」命令只能看到 calldemo.abi 文件,參 考效果如下:
步驟 02:獲得合約地址。注意要將合約部署到 Geth 節點。因此 Environment 選擇為 Web3 Provider。
在【Environment】選項框中選擇「Web3 Provider」,然後單擊【Deploy】按鈕。
部署後,獲得合約地址為:。
步驟 03:利用 abigen 工具(Geth 工具包內的可執行程序)編譯智能合約為 Go 代碼。abigen 工具的作用是將 abi 文件轉換為 Go 代碼,命令如下:
其中各參數的含義如下。 (1)abi:是指定傳入的 abi 文件。 (2)type:是指定輸出文件中的基本結構類型。 (3)pkg:指定輸出文件 package 名稱。 (4)out:指定輸出文件名。 執行後,將在代碼目錄下看到 funcdemo.go 文件,讀者可以打開該文件欣賞一下,注意不要修改它。
步驟 04:創建 main.go,填入如下代碼。 注意代碼中 HexToAddress 函數內要傳入該合約部署後的地址,此地址在步驟 01 中獲得。
步驟 04:設置 go mod,以便工程自動識別。
前面有所提及,若要使用 Go 語言調用智能合約,需要下載 go-ethereum 工程,可以使用下面 的指令:
該指令會自動將 go-ethereum 下載到「$GOPATH/src/github.com/ethereum/go-ethereum」,這樣還算 不錯。不過,Go 語言自 1.11 版本後,增加了 mole 管理工程的模式。只要設置好了 go mod,下載 依賴工程的事情就不必關心了。
接下來設置 mole 生效和 GOPROXY,命令如下:
在項目工程內,執行初始化,calldemo 可以自定義名稱。
步驟 05:運行代碼。執行代碼,將看到下面的效果,以及最終輸出的 2020。
上述輸出信息中,可以看到 Go 語言會自動下載依賴文件,這就是 go mod 的神奇之處。看到 2020,相信讀者也知道運行結果是正確的了。
7. 以太坊event log查詢與解析
從 ethereum json-rpc文檔 的文檔中找到一個同時指定多個事件以 OR 或者 AND 查詢的方法.以下是查詢 Approval 或 Transfer 事件的方法:
topics 欄位中指定查詢條件的語法參考上面鏈接。
通過 getTransactionReceipt 在ropsten測試網上查詢到交易號為 的交易詳情
這個交易從 "from": "" 發送到合約地址 "to": "" .這個合約為ERC20代幣合約.從 topics 的第一個元素可以看出合約中產生了 Transfer 事件(topics第一個元素一定是事件的keccak哈希). topics 的第二個欄位是轉出代幣的地址,第三個欄位是接收者地址.ERC20代幣 Transfer 事件的簽名為
我們注意到 Transfer 事件的第一個和第二個參數被標記為 indexed , 因此他們的值被放在 topics array 中. 由於tokens參數沒有標記為 indexed , 所以他的值被放在 data 欄位. 如果事件中有多個欄位未標記為 indexed , 那麼他們的值都會被記錄在 data 欄位中。
8. 《區塊鏈項目開發指南》讀書筆記
ethash
答:在DAPP中,沒有一個中心伺服器來協調節點,或者決定什麼是對,什麼是錯,因此應對這個挑戰確實不容易,一致性協議(concensus protocol)可用於解決這個問題。
補充:共識演算法的核心就是解決拜占庭將軍問題(分布式網路一致性問題)。
答:修改bug或者更新DAPP很困難。
如果我需要從一個中心化應用抓取數據,如車輛違章信息,怎麼保證抓取的數據是真實有效的?
答:為了訪問中心化的API,可以使用Oraclize服務可以作為中間人,Oraclize為從中心化服務智能合約中抓取的數據提供TLSNotary驗證。
中心化應用的所有者需要有盈利才能長期維護應用的運行,而DAPP雖然沒有所有者,但是跟中心化應用一樣,DAPP節點需要硬體和網路資源才能維持運行。DAPP節點需要一些有用的回報來維持運行,於是內部貨幣登場了。大多數DAPP都有內置內部貨幣,或者可以說最成功的DAPP都有內置內部貨幣。如以太幣
授權的DAPP不對所有人開放。授權的DAPP繼承了免許可權DAPP的全部屬性,但需要許可權才能參與到網路中去。授權的DAPP與免許可權的DAPP的共識協議是不同的。授權的DAPP沒有內部貨幣。
超級賬本(Hyperledger)項目致力於開發創建授權的DAPP技術。
為什麼少數國家認定比特幣是非法的,大部分國家對此還沒有做出決定呢?原因如下:
星際文件存儲系統(InterPlanetary File System)是一個去中心化的文件系統。
目標是通過使交易幾乎瞬間完成,並隱藏交易賬戶的信息,還可以防止他人用ISP追蹤所有者。
任何人都可以成為以太坊網路中的礦工。每個礦工獨自解決問題,第一個解決問題的礦工是勝利者,它得到的回報是5個以太幣和該區塊中全部交易的交易費。區塊鏈中有多少個區塊沒有限制,可以生成的以太幣總數也沒有限制。
網路中的任何節點都可以檢查區塊鏈是否合法,首先檢查交易在區塊鏈中是否合法以及時間戳的驗證情況,然後檢查區塊的目標值和隨機數是否合法、礦工是否得到合法的回報等。
節點是如何發現網路中的其他節點的呢?
以太坊的節點發現協議:Kadelima,在這種協議中,有一種特殊節點Bootstrap節點。它保存了一段時間內與它連接的所有節點列表,但其本身不保存區塊鏈。
當對等節點連接到以太坊網路時,它們首先連接到Bootstrap節點。
可以有多種以太坊實例,也就是說,不同的網路每個都有自己的網路ID。
兩種主要的以太坊網路是主網和測試網。以太幣在主網上交易,而測試網供開發人員測試。
一個去中心化的通信協議,它支持廣播、用戶到用戶、加密信息等,但不用於傳輸大數據。
一個去中心化的文件系統。
geth為其他應用提供了與其通信的JSON-RPC API。使用HTTP、WebSocket和其他協議服務於JSON-RPC API。
JSON-RPC API提供的API分成如下類型:
以太坊網路中的節點默認用 30303 埠通信。
--networkid 用於指定網路ID,1代表主網網路ID,預設默認值為1,2代表測試網路ID
--dev 標記運行一個私有網路
--etherbase 指定挖礦賺取的回報存入的錢包地址
--unlock 解鎖一個或者多個賬戶
以太坊錢包與geth捆綁在一起。運行以太坊時,它會嘗試發現一個本地geth實例並與之連接;如果它不能發現geth正在運行,它就啟動自己的geth節點。以太坊錢包使用IPC與geth通信。geth支持以文件為基礎的IPC。
以太坊下一個主要更新的名字。Serenity把共識協議改為casper,並將整合狀態通道和分片。
Casper 實施了一個進程,使得它可以懲罰所有的惡意因素。這就是權益證明在Casper下是如何工作的:
驗證者押下一定比例的他們擁有的以太幣作為保證金。然後,他們將開始驗證區塊。也就是說,當他們發現一個可以他們認為可以被加到鏈上的區塊的時候,他們將以通過押下賭注來驗證它。
如果該區塊被加到鏈上,然後驗證者們將得到一個跟他們的賭注成比例的獎勵。但是,如果一個驗證者採用一種惡意的方式行動、試圖做「無利害關系」的事,他們將立即遭到懲罰,他們所有的權益都會被砍掉。正如你可以看到的,Casper被設計成可以在一個無需信任的系統上工作,並且是更加拜占庭容錯的。
支付通道 功能允許將兩個以上向另一個賬戶發送以太幣的交易合並成兩個交易。其工作原理為:假設X是一個視頻網站老闆,Y是個用戶。X每分鍾收費1個以太幣。現在X想讓Y看視頻期間每分鍾交一次錢。當然,Y可以每分鍾廣播交易,但是這里有些問題,例如X不得不等待確認,所以視頻就會中斷一會。支付通道可以解決這個問題。使用支付通道,Y可以廣播一個鎖定交易,為X把一些以太幣(比如100個以太幣)鎖定一段時間(比如24小時)。現在每看完一分鍾視頻,Y將發送一個簽名記錄表示可以解鎖,一個以太幣就進入X的賬戶,其餘的進入Y的賬戶。再過一分鍾,Y將發送一個簽名記錄表示可以解鎖,兩個以太幣就進入X的賬戶,其餘的進入Y的賬戶。Y觀看X網站的視頻過程中,該過程將持續。現在假設Y看完了100小時視頻或者24小時時間到了,X將向網路廣播最後的簽名記錄,以把錢收到自己的賬戶里。如果X沒有在24小時內提款,全款會返還給Y。所以在區塊鏈中,我們將看到lock和unlock兩種交易。
Sybil攻擊
51%攻擊
補充:不能存儲較大數據,目前有Swarm與IPFS等分布式存儲方式可供選擇
把所有東西都存在內存里,因此,節點一旦重啟,將丟失以前的狀態。
默認監聽埠:8545
9. Dubbo——HTTP 協議 + JSON-RPC
Protocol 還有一個實現分支是 AbstractProxyProtocol,如下圖所示:
從圖中我們可以看到:gRPC、HTTP、WebService、Hessian、Thrift 等協議對應的 Protocol 實現,都是繼承自 AbstractProxyProtocol 抽象類。
目前互聯網的技術棧百花齊放,很多公司會使用 Node.js、Python、Rails、Go 等語言來開發 一些 Web 端應用,同時又有很多服務會使用 Java 技術棧實現,這就出現了大量的跨語言調用的需求。Dubbo 作為一個 RPC 框架,自然也希望能實現這種跨語言的調用,目前 Dubbo 中使用「HTTP 協議 + JSON-RPC」的方式來達到這一目的,其中 HTTP 協議和 JSON 都是天然跨語言的標准,在各種語言中都有成熟的類庫。
下面就重點來分析 Dubbo 對 HTTP 協議的支持。首先,會介紹 JSON-RPC 的基礎,並通過一個示例,快速入門,然後介紹 Dubbo 中 HttpProtocol 的具體實現,也就是如何將 HTTP 協議與 JSON-RPC 結合使用,實現跨語言調用的效果。
Dubbo 中支持的 HTTP 協議實際上使用的是 JSON-RPC 協議。
JSON-RPC 是基於 JSON 的跨語言遠程調用協議。Dubbo 中的 bbo-rpc-xml、bbo-rpc-webservice 等模塊支持的 XML-RPC、WebService 等協議與 JSON-RPC 一樣,都是基於文本的協議,只不過 JSON 的格式比 XML、WebService 等格式更加簡潔、緊湊。與 Dubbo 協議、Hessian 協議等二進制協議相比,JSON-RPC 更便於調試和實現,可見 JSON-RPC 協議還是一款非常優秀的遠程調用協議。
在 Java 體系中,有很多成熟的 JSON-RPC 框架,例如 jsonrpc4j、jpoxy 等,其中,jsonrpc4j 本身體積小巧,使用方便,既可以獨立使用,也可以與 Spring 無縫集合,非常適合基於 Spring 的項目。
下面先來看看 JSON-RPC 協議中請求的基本格式:
JSON-RPC請求中各個欄位的含義如下:
在 JSON-RPC 的服務端收到調用請求之後,會查找到相應的方法並進行調用,然後將方法的返回值整理成如下格式,返回給客戶端:
JSON-RPC響應中各個欄位的含義如下:
Dubbo 使用 jsonrpc4j 庫來實現 JSON-RPC 協議,下面使用 jsonrpc4j 編寫一個簡單的 JSON-RPC 服務端示常式序和客戶端示常式序,並通過這兩個示常式序說明 jsonrpc4j 最基本的使用方式。
首先,需要創建服務端和客戶端都需要的 domain 類以及服務介面。先來創建一個 User 類,作為最基礎的數據對象:
接下來創建一個 UserService 介面作為服務介面,其中定義了 5 個方法,分別用來創建 User、查詢 User 以及相關信息、刪除 User:
UserServiceImpl 是 UserService 介面的實現類,其中使用一個 ArrayList 集合管理 User 對象,具體實現如下:
整個用戶管理業務的核心大致如此。下面我們來看服務端如何將 UserService 與 JSON-RPC 關聯起來。
首先,創建 RpcServlet 類,它是 HttpServlet 的子類,並覆蓋了 HttpServlet 的 service() 方法。我們知道,HttpServlet 在收到 GET 和 POST 請求的時候,最終會調用其 service() 方法進行處理;HttpServlet 還會將 HTTP 請求和響應封裝成 HttpServletRequest 和 HttpServletResponse 傳入 service() 方法之中。這里的 RpcServlet 實現之中會創建一個 JsonRpcServer,並在 service() 方法中將 HTTP 請求委託給 JsonRpcServer 進行處理:
最後,創建一個 JsonRpcServer 作為服務端的入口類,在其 main() 方法中會啟動 Jetty 作為 Web 容器,具體實現如下:
這里使用到的 web.xml 配置文件如下:
完成服務端的編寫之後,下面再繼續編寫 JSON-RPC 的客戶端。在 JsonRpcClient 中會創建 JsonRpcHttpClient,並通過 JsonRpcHttpClient 請求服務端:
在 AbstractProxyProtocol 的 export() 方法中,首先會根據 URL 檢查 exporterMap 緩存,如果查詢失敗,則會調用 ProxyFactory.getProxy() 方法將 Invoker 封裝成業務介面的代理類,然後通過子類實現的 doExport() 方法啟動底層的 ProxyProtocolServer,並初始化 serverMap 集合。具體實現如下:
在 HttpProtocol 的 doExport() 方法中,與前面介紹的 DubboProtocol 的實現類似,也要啟動一個 RemotingServer。為了適配各種 HTTP 伺服器,例如,Tomcat、Jetty 等,Dubbo 在 Transporter 層抽象出了一個 HttpServer 的介面。
bbo-remoting-http 模塊的入口是 HttpBinder 介面,它被 @SPI 註解修飾,是一個擴展介面,有三個擴展實現,默認使用的是 JettyHttpBinder 實現,如下圖所示:
HttpBinder 介面中的 bind() 方法被 @Adaptive 註解修飾,會根據 URL 的 server 參數選擇相應的 HttpBinder 擴展實現,不同 HttpBinder 實現返回相應的 HttpServer 實現。HttpServer 的繼承關系如下圖所示:
這里以 JettyHttpServer 為例簡單介紹 HttpServer 的實現,在 JettyHttpServer 中會初始化 Jetty Server,其中會配置 Jetty Server 使用到的線程池以及處理請求 Handler:
可以看到 JettyHttpServer 收到的全部請求將委託給 DispatcherServlet 這個 HttpServlet 實現,而 DispatcherServlet 的 service() 方法會把請求委託給對應接埠的 HttpHandler 處理:
了解了 Dubbo 對 HttpServer 的抽象以及 JettyHttpServer 的核心之後,回到 HttpProtocol 中的 doExport() 方法繼續分析。
在 HttpProtocol.doExport() 方法中會通過 HttpBinder 創建前面介紹的 HttpServer 對象,並記錄到 serverMap 中用來接收 HTTP 請求。這里初始化 HttpServer 以及處理請求用到的 HttpHandler 是 HttpProtocol 中的內部類,在其他使用 HTTP 協議作為基礎的 RPC 協議實現中也有類似的 HttpHandler 實現類,如下圖所示:
在 HttpProtocol.InternalHandler 中的 handle() 實現中,會將請求委託給 skeletonMap 集合中記錄的 JsonRpcServer 對象進行處理:
skeletonMap 集合中的 JsonRpcServer 是與 HttpServer 對象一同在 doExport() 方法中初始化的。最後,我們來看 HttpProtocol.doExport() 方法的實現:
介紹完 HttpProtocol 暴露服務的相關實現之後,下面再來看 HttpProtocol 中引用服務相關的方法實現,即 protocolBindinRefer() 方法實現。該方法首先通過 doRefer() 方法創建業務介面的代理,這里會使用到 jsonrpc4j 庫中的 JsonProxyFactoryBean 與 Spring 進行集成,在其 afterPropertiesSet() 方法中會創建 JsonRpcHttpClient 對象:
下面來看 doRefer() 方法的具體實現:
在 AbstractProxyProtocol.protocolBindingRefer() 方法中,會通過 ProxyFactory.getInvoker() 方法將 doRefer() 方法返回的代理對象轉換成 Invoker 對象,並記錄到 Invokers 集合中,具體實現如下:
本文重點介紹了在 Dubbo 中如何通過「HTTP 協議 + JSON-RPC」的方案實現跨語言調用。首先介紹了 JSON-RPC 中請求和響應的基本格式,以及其實現庫 jsonrpc4j 的基本使用;接下來我們還詳細介紹了 Dubbo 中 AbstractProxyProtocol、HttpProtocol 等核心類,剖析了 Dubbo 中「HTTP 協議 + JSON-RPC」方案的落地實現。
10. Infura API 獲取以太坊當前配置鏈 ID - 區塊鏈數據開發實戰
簡介:Infura 是以太坊和 IPFS 的 API 服務提供商。Infura 一開始只是為 ConsenSys 內部項目提供穩定可靠的 RPC 訪問,後來隨著以太坊生態發展,他們意識到自己可以起到更大作用,於是開始面向開發者提供公共 API 服務。本文整理使用 Infura API 獲取以太坊當前配置鏈 ID 的實現。
Infura 是以太坊和 IPFS 的 API 服務提供商。Infura 一開始只是為 ConsenSys 內部項目提供穩定可靠的 RPC 訪問,後來隨著以太坊生態發展,他們意識到自己可以起到更大作用,於是開始面向開發者提供公共 API 服務。
本文整理使用 Infura API 獲取以太坊當前配置鏈 ID 的實現。
Infura API 官方文檔: https://infura.io/docs
使用 API 需要申請 Project ID ,ID 是免費申請的,申請流程為「注冊 - 登錄 - 創建新項目」,不需要審核,幾分鍾就能搞定。
Infura API 標准請求埠格式:
本例中我們使用基於 HTTP 的以太坊主網 JSON-RPC 埠:
Infura API 獲取以太坊當前配置鏈 ID:
Curl 示例:
Node.js 示例:
返回的 JSON 示例:
返回當前鏈 ID 的大整數。
Infura API 服務思維導圖:
我們有一個區塊鏈知識星球,做區塊鏈前沿資料的歸納整理以方便大家檢索查詢使用,也是國內頂尖區塊鏈技術社區,歡迎感興趣的朋友加入。如果你對上面內容有疑問,也可以加入知識星球提問我: