官方接口訪問庫 GitHub : https://github.com/bitshares/bitsharesjs-ws 在閱讀源碼之前,最好先瀏覽一遍 Bitshares 官方公開的開發(fā)文檔,雖然文檔中對開發(fā)細節(jié)描述的不是很清楚,但也能對系統(tǒng)的通訊方式有個詳細的了解。
項目庫介紹
bitsharesjs-ws
是比特股的 Github 官方賬號創(chuàng)建的一個項目,主要功能是提供一個訪問比特股 API 節(jié)點的工具,與 API 節(jié)點通訊訪問方式采用的是 WebSocket 方式,代碼采用JS編寫,可以同時運行于瀏覽器端與Node環(huán)境下。
項目代碼結構如下:
1 2 3 4 5 + build * 編譯版本的項目文件 + examples * 項目庫的使用案例 + lib * 項目庫源碼文件 + test * 項目單元測試 + tools * 編譯工具
直接進入 lib 目錄,打開 index.js
文件閱讀源碼,你會發(fā)現(xiàn)項目庫共導出三個如下模塊:
1 2 3 4 5 export { Apis, // 工具庫的主要接口,對外公開了 API 的訪問方式,與節(jié)點服務器的通訊全部依賴此模塊的內部實現(xiàn) ChainConfig, // Bitshares 區(qū)塊鏈公開的參數(shù)配置項 Manager // 與節(jié)點服務器之間的連接管理器,負責管理與檢查和節(jié)點服務器之間的連接狀態(tài) }
Apis 模塊實現(xiàn)邏輯
Apis 模塊對應的實現(xiàn)文件是 ApisInstances.js
,該模塊引入了 ChainWebSocket.js
、GrapheneApi.js
等內部模塊,這些內部子模塊的作用稍后再作描述,先看Apis模塊導出的供外部系統(tǒng)調用的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 export default { /** * String cs 代表要連接的完整錢包節(jié)點地址。 * Boolean connect 創(chuàng)建成功之后是否直接執(zhí)行創(chuàng)建的對象的 connect 方法。 * int connectTimeout 連接節(jié)點時,響應超時的時間。 * Boolean enableCrypto 是否進行加密通訊。 * * @return 該方法會返回一個 ApiInstance 的實例對象,全局共享此單例對象。 */ instance (cs = "ws://localhost:8090", connect, connectTimeout = 4000, enableCrypto) /** * Function callback 設置一個當網絡連接狀態(tài)發(fā)生改變的時候的回調函數(shù)。 */ setRpcConnectionStatusCallback (callback) /** * 設定當網絡連接失敗的時候,是否自動重連,會重新設置 autoReconnect 的值。 */ setAutoReconnect (auto) /** * 重置當前的連接,假如當前是連接狀態(tài),會直接 */ reset (cs = "ws://localhost:8090", connect, connectTimeout = 4000) /** * 獲取當前連接的服務器節(jié)點的區(qū)塊鏈 ID */ chainId () /** * 關閉當前的連接 */ close () }
查看上面導出的方法實現(xiàn),可以發(fā)現(xiàn) instance 方法會構建一個 ApisInstance 對象的實例,并讓外部系統(tǒng)該通過此實例調用對應類別的API接口,關于 ApisInstance 對象的定義如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Class ApiInstance { chain_id // 當前連接的區(qū)塊鏈 id String url // 當前連接的完整錢包節(jié)點地址。 statusCb ChainWebSocket ws_rpc // 創(chuàng)建的 ChainWebSocket 對象實例。 Promise init_promise GrapheneApi _db GrapheneApi _net GrapheneApi _hist GrapheneApi _crypt /** * 調用 instance() 的時候,第二個參數(shù)傳遞 true,會自動調用此函數(shù)連接區(qū)塊節(jié)點,旨在初始化網絡連接。 * 并且初始化 inst 內部的各個關鍵屬性,init_promise, _db, ws_rpc ... */ connect (cs, connectTimeout, enableCrypto = false) close() db_api() network_api() history_api() crypto_api() setRpcConnectionStatusCallback() }
此處方法的功能是與 Apis 模塊提供的那些同名方法是相同的,因為 Apis 模塊公開的那些方法,也是最終的中轉到了此處的方法實現(xiàn)。重點需要注意的就是此對象的內部屬性,主要有ws_rpc
、init_promise
、_db
、_net
、_hist
、_crypt
等。
ws_rpc
此屬性的值是一個 ChainWebSocket 對象的實例,內部包裝了 ReconnectWebSocket 對象,并定義了一套API接口的調用流程。最終由此對象向服務器節(jié)點發(fā)起 API 調用,并處理返回結果,這樣一個完整的訪問流程,就是此內部模塊定義的。
init_promise
一個Promise對象,因為網絡接口調用、WebSocket連接等操作都是異步響應的,所以此處定義了一個 Promise 對象來給外部系統(tǒng)使用,當與服務器節(jié)點之間的連接全部初始化完成之后,此異步對象就會變?yōu)橥瓿蔂顟B(tài)。
_db
_net
_hist
_crypt
這些屬性都分別是一個 GrapheneApi 對象的實例,用于區(qū)分不同類別的API調用,因為 Bitshares 系統(tǒng)為不同的 API 接口設置有不同的 API 訪問令牌,所以單獨為每個 API 類型都創(chuàng)建了一個實例,以便外部系統(tǒng)調用不同類型的 API 的時候,不用反復設置 API 令牌標識。
關于 ChainWebSocket 與 ReconnectWebSocket 應該進一步解釋下它們的作用,首先說一下 ReconnectWebSocket, ReconnectWebSocket 實現(xiàn)了在瀏覽器環(huán)境下的斷線重連機制,因為整個模塊是使用 WbeSocket 與服務節(jié)點進行通信連接的,所以可能會偶爾的發(fā)生 WebSocket 斷開連接的問題。
因為當斷開連接以后,可能就會導致訂閱通知與API調用接口不能正常使用,這就需要外部系統(tǒng)自己再定義一套復雜的連接狀態(tài)檢查規(guī)則,來進行斷線之后的連接與初始化操作。所以索性就在模塊內部實現(xiàn)了一個 ReconnectWebSocket 模塊,實現(xiàn)一套 Bitshares 系統(tǒng)專用的斷線重連的制度,來保證與服務器節(jié)點連接的穩(wěn)定性,減少外部系統(tǒng)的復雜程度,讓外部系統(tǒng)能專心于業(yè)務邏輯的開發(fā)。
具體是如何定義重連制度的,可以自己查看 ReconnectWebSocket 模塊的源碼,這里不再詳細介紹,但其實官方引用的 ReconnectWebSocket 在實現(xiàn)的時候也有一個問題,ReconnectWebSocket 僅僅是包裝了 WebSocket 對象,并在內部依賴 document 與 window 對象,所以導致斷線重連的制度在Nodejs的環(huán)境中是不可用的,只有在瀏覽器的環(huán)境下才會有斷線重連保護。
ChainWebSocket 是建立在可靠的 ReconnectWebSocket 連接之上的,然后針對 Bitshares 系統(tǒng)的特性,構建了一個API的訪問流程,和訂閱通知的處理流程。外部系統(tǒng)的所有API操作操作都會經由 ChainWebSocket 處理并通過 WebSocket 發(fā)送到服務器節(jié)點,然后在響應結果發(fā)生以后,再由 ChainWebSocket 解析并轉交給外外部系統(tǒng)處理。
由于在 Nodejs 的環(huán)境中是不存在 WebSocket 對象的,所以引入了一個開源的第三方 WebSocket 的實現(xiàn) —— ws
:
1 2 3 4 5 6 7 if (typeof WebSocket === "undefined" && !process.env.browser) { WebSocketClient = require("ws"); } else if (typeof(WebSocket) !== "undefined" && typeof document !== "undefined") { WebSocketClient = require("ReconnectingWebSocket") } else { WebSocketClient = WebSocket; }
那么 Apis 模塊的功能就已經全部介紹完了,梳理一下你的 API 訪問流程應該是這樣的:
通過 Apis.instance 連接操作,告訴模塊要到的服務器節(jié)點,并且配置是否開啟斷線重連制度。
然后通過返回的 instance 對象屬性 init_promise 來指定連接成功之后的下一步操作。
假如你在連接成功之后通過 instance.__db 屬性調用了 get_account
API,那么你的請求會經由 GrapheneApi 打上對應的API令牌標識,再投遞給 ChainWebSocket 發(fā)送請求,當響應結果到來的時候,又會回調你的處理函數(shù)來完成一個完整的API請求流程。
Manager 模塊實現(xiàn)邏輯
輔助 Apis 模塊實現(xiàn) WebSocket 連接的管理,更詳細的功能不再描述,可以自行查看源碼。
Manager 模塊實現(xiàn)邏輯
定義了一些 Bitshares 鏈上的一些屬性配置,我目前對 Bitshares 系統(tǒng)的其他細節(jié)還不了解,就不作解讀說明了,等以后學習了更多的知識,再補充這些內容供大家參考。
總結
bitsharesjs-ws
針對 Bitshares 系統(tǒng),定義了一套 API 調用的流程,并拓展了 WebSocket 的功能來維持一個穩(wěn)定的連接通道,讓外部系統(tǒng)能夠專心于業(yè)務 API 的開發(fā),但在其模塊職責領域上,還應該有很多的可拓展空間,在此感謝此模塊開發(fā)者為我們提供了一個好用的工具庫,并節(jié)省了我們寶貴的時間。