淺析以太坊網絡狀態平臺架構及 WebSocket

最近有幸成為數字人民幣的用戶,可以使用數字人民幣App來進行支付。這里發幾個截圖看看數字人民幣長什么樣子。

說到數字人民幣不得不說區塊鏈,今天就跟大家分享一篇2018年寫的學習筆記。
概念普及
說到區塊鏈,需要聲明三個概念的是區塊鏈、虛擬貨幣、數字貨幣,區塊鏈不等于虛擬貨幣,虛擬貨幣也不等同于數字貨幣。
常說的區塊鏈是指其技術,是一種全新的分布式數據管理方式;提起區塊鏈,很多人就會將其與虛擬火幣“比特幣”、“以太坊”、“狗狗幣”等虛擬貨幣相關聯,這些只能視為區塊鏈技術的“衍生產品”之一,因此絕不能將區塊鏈 “簡單粗暴”地等同于虛擬貨幣,在我國提升大量發展區塊鏈技術,而非虛擬貨幣,虛擬貨幣在我國是禁止的。
開頭說的數字人民幣,是央行發行的數字貨幣,屬于央行負債,具有國家信用背書,與法定貨幣等值。其功能屬性與紙鈔完全一樣,只不過是數字化形態。數字貨幣首先是貨幣,而虛擬貨幣只是一種代幣,沒有貨幣屬性,只有狹義的價值(也可以說是共識價值)。
什么是區塊鏈技術?
那么現在我們說回區塊鏈技術,那什么是區塊鏈技術?
區塊鏈是一種去中心化的數字交易分類賬,是一個不斷增長的電子記錄列表,它將被長期保留,并且通過加密術(一種算法代碼)保證其安全。區塊鏈分類帳數據分布在計算機網絡中。用戶可以直接與存儲的數據進行實時交互,而無需中介(“中間人”或分銷商)來驗證交易。該技術為區塊鏈中的各方提供了一個獨立、防篡改和透明的平臺,可安全地存儲、傳輸和處理敏感信息。
說完什么是區塊鏈技術,現在我們來聊聊前端可以從區塊鏈技術中學到什么,區塊鏈項目,有一個不錯的屬性就是開源,因此我們就能從開源代碼中學習一些技術知識。下面就拿以太坊網絡狀態可視化項目學習其架構和WebSocket的應用。
以太坊網絡狀態平臺EthStats
以太坊網絡狀態(EthStats)是用于跟蹤以太坊網絡狀態的可視化平臺,使用WebSockets技術從正在運行中的節點中接收統計信息并通過不同的規則來輸出數據,用戶可以通過該網站直觀的了解以太坊節點的運行情況。
聲明:下面的內容寫于2018年,因此界面跟現在可能有差異,但架構及技術本身還是有學習意義。
Ethstats監控節點信息(節點名稱、節點運行環境、運行時間、算力情況、Peer數量、Pending交易數量、最新區塊號和HASH、總難度、區塊交易)、網絡平均HASHRATE、Gas Limit、活躍節點數量、叔區塊數據都可以通該網站實時獲取。本文介紹整個平臺的實現方案,能夠為我們提供一些實現參考,進而加深對WebSocket技術的了解。

整體結構

平臺涉及兩個項目
Ethereum Network Intelligence API
后端服務,與以太坊節點一起運行并跟蹤網絡狀態,通過JSON-RPC同步信息,再通過WebSockets連接到eth-netstats同步信息。下面介紹一些關鍵的實現。
-
判斷節點是否在線:通過websocket定時(3秒)向EthStats發送
node-ping消息,并監聽一個pong消息來接收響應,以此來確定節點的狀態Node.prototype.ping = function () { this._latency = _.now(); socket.emit("node-ping", { id: this.id, clientTime: _.now(), }); }; // 只是貼出監聽的代碼 socket.on("node-pong", function (data) { var now = _.now(); var latency = Math.ceil((now - data.clientTime) / 2); socket.emit("latency", { id: self.id, latency: latency, }); }); ``` -
其他數據同步消息
// 區塊更新 this.emit("block", this.prepareBlock()); // 發送pending交易數量 this.emit("pending", this.preparePending()); // 發送統計數據 this.emit("stats", stats); // 統計節點數據結構 this.stats = { active: false, mining: false, hashrate: 0, peers: 0, pending: 0, gasPrice: 0, block: { number: 0, hash: "?", difficulty: 0, totalDifficulty: 0, transactions: [], uncles: [], }, syncing: false, uptime: 0, }; ```
Ethereum Network Stats(EthStats)
項目使用WebSockets從正在運行的節點接收統計數據,并通過websocket接口輸出到前端呈現,因此有兩個websocket接口,一個用于接收Ethereum Network Intelligence同步過來的節點數據,默認地址:ws://localhost:3000/api;一個用于前端調用呈現數據,默認地址:ws://localhost:3000/primus
接收Ethereum Network Intelligence API發送過來的數據后會進行一些數據處理再發送到前端呈現。封裝的數據包括:
// client 為前端websocket對象
// 繪制圖表數據
client.write({
action: "charts",
data: history,
});
// 增加節點信息
client.write({
action: "add",
data: info,
});
// 更新節點信息
client.write({
action: "update",
data: stats,
});
// 同步區塊信息
client.write({
action: "block",
data: stats,
});
// 同步pengding交易數
client.write({
action: "pending",
data: stats,
});
// 同步節點狀態
client.write({
action: "inactive",
data: stats,
});
WebSocket及兼容性
WebSocket介紹
WebSocket是一種在單個TCP連接上進行全雙工通信的協議,使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。
ws協議默認使用80端口,wss協議默認使用443端口。
WebSocket與HTTP的關系
同樣作為應用層的協議,WebSocket在現代的軟件開發中被越來越多的實踐,和HTTP有很多相似的地方。
-
相同點
- 都是一樣基于TCP的,都是可靠性傳輸協議。
- 都是應用層協議。
-
不同點
- WebSocket是雙向通信協議,模擬Socket協議,可以雙向發送或接受信息。HTTP是單向的。
- WebSocket是需要瀏覽器和服務器握手進行建立連接的。而http是瀏覽器發起向服務器的連接,服務器預先并不知道這個連接。
-
聯系:WebSocket在建立握手時,數據是通過HTTP傳輸的。建立之后,在真正傳輸時候是不需要HTTP協議的。
為什么要用WebSocket來替代HTTP
WebSocket的目的是為了解決網絡傳輸中的雙向通信的問題,HTTP1.1默認使用持久連接(persistent connection),在一個TCP連接上也可以傳輸多個Request/Response消息對,但是HTTP的基本模型還是一個Request對應一個Response。這在雙向通信時一般會使用以下幾種解決方案:
-
輪詢(polling):不管服務器端有沒有更新,客戶端每隔一個時間段就向服務器發送一個請求,結果可能是服務器端有新的更新過來,也可能什么也沒有,只是返回個空的信息。不管結果如何,客戶端處理完后到下一個定時時間點將繼續下一輪的輪詢。缺點浪費大量流量(請求中有大半是無用),對服務端造成了巨大壓力,且并非實時。 -
長連接或長輪詢(Long-Polling):客戶端在發起一次請求后立即掛起,一直到服務器端有更新的時候,服務器才會主動推送信息到客戶端。 在服務器端有更新并推送信息過來之前這個周期內,客戶端不會有新的多余的請求發生,服務器端對此客戶端也啥都不用干,只保留最基本的連接信息,一旦服務器有更新將推送給客戶端,客戶端將相應的做出處理,處理完后再重新發起下一輪請求,缺點會造成服務器hold連接會消耗資源,返回數據順序無保證,難于管理維護。長連接的實現方式由兩種:-
基于Ajax的長輪詢(long-polling)方式:瀏覽器發出XMLHttpRequest請求,服務器端接收到請求后,會阻塞請求直到有數據或者超時才返回,瀏覽器在處理請求返回信息(超時或有效數據)后再次發出請求,重新建立連接。在此期間服務器端可能已經有新的數據到達,服務器會選擇把數據保存,直到重新建立連接,瀏覽器會把所有數據一次性取回。 -
基于Iframe及htmlfile的流(http streaming)方式:通常的做法是在頁面中嵌入一個隱藏的iframe,然后讓這個iframe的src屬性指向我們請求的一個服務端地址,并且為了數據更新,將頁面上數據更新操作封裝為一個js函數,將函數名當做參數傳遞到這個地址當中。服務端收到請求后解析地址取出參數(客戶端js函數調用名),每當有數據更新的時候,返回對客戶端函數的調用,并且將要跟新的數據以js函數的參數填入到返回內容當中,例如返回:<script type="text/javascript">window.parent("data")</script>
意味著以data為參數調用客戶端update函數進行客戶端view更新。
-
相比于間斷的輪詢或長輪詢來模擬全雙工連接的解決方式,Websocket極大的減少了不必要的網絡流量和延遲。除此之外,Websocket的應用減輕了服務器的負擔,讓現有的機器能支持更多的并發連接。如下圖所示:

WebSocket兼容性
隨著HTML5的普及,現代瀏覽器(IE10+)基本上都已經原生支持WebSocket了,下面是支WebSocket協議的瀏覽器:

簡單的實現
Node.js編寫服務端可以用ws這個庫。在Ethstats中使用的是primus,而ws實現更輕量,更適合寫一個簡單的實現。
-
服務端
代碼如下,監聽3000端口。當有新的連接請求到達時,打印日志,同時向客戶端發送消息。當收到到來自客戶端的消息時,同樣打印日志。var WebSocket = require('ws'); var wss = new WebSocket.Server({ port: 3000 }); wss.on('connection', function connection(ws) { console.log('Server:Starting socket connection'); ws.on('message', function incoming(message) { console.log('Server: Received: %s', message); }); ws.send('world'); }); -
客戶端
代碼如下,向3000端口發起WebSocket連接,打印日志。<html> <head> <title>WebSocket</title> <script> var ws = new WebSocket('ws://localhost:3000'); ws.onopen = function () { console.log('Ws Onopen'); ws.send('From Client: Hello'); }; ws.onmessage = function (e) { console.log('ws onmessage'); console.log('From Server: ' + e.data); }; </script> </head> </html>
總結
區塊鏈項目開源的屬性,讓對技術有著好奇的我不會放過學習的機會。