浅析以太坊网络状态平台架构及 WebSocket

浅析以太坊网络状态平台架构及WebSocket

最近有幸成为数字人民币的用户,可以使用数字人民币App来进行支付。这里发几个截图看看数字人民币长什么样子。

数字人民币App

说到数字人民币不得不说区块链,今天就跟大家分享一篇2018年写的学习笔记。

概念普及

说到区块链,需要声明三个概念的是区块链、虚拟货币、数字货币,区块链不等于虚拟货币,虚拟货币也不等同于数字货币。

常说的区块链是指其技术,是一种全新的分布式数据管理方式;提起区块链,很多人就会将其与虚拟火币“比特币”、“以太坊”、“狗狗币”等虚拟货币相关联,这些只能视为区块链技术的“衍生产品”之一,因此绝不能将区块链 “简单粗暴”地等同于虚拟货币,在我国提升大量发展区块链技术,而非虚拟货币,虚拟货币在我国是禁止的。

开头说的数字人民币,是央行发行的数字货币,属于央行负债,具有国家信用背书,与法定货币等值。其功能属性与纸钞完全一样,只不过是数字化形态。数字货币首先是货币,而虚拟货币只是一种代币,没有货币属性,只有狭义的价值(也可以说是共识价值)。

什么是区块链技术?

那么现在我们说回区块链技术,那什么是区块链技术?

区块链是一种去中心化的数字交易分类账,是一个不断增长的电子记录列表,它将被长期保留,并且通过加密术(一种算法代码)保证其安全。区块链分类帐数据分布在计算机网络中。用户可以直接与存储的数据进行实时交互,而无需中介(“中间人”或分销商)来验证交易。该技术为区块链中的各方提供了一个独立、防篡改和透明的平台,可安全地存储、传输和处理敏感信息。

说完什么是区块链技术,现在我们来聊聊前端可以从区块链技术中学到什么,区块链项目,有一个不错的属性就是开源,因此我们就能从开源代码中学习一些技术知识。下面就拿以太坊网络状态可视化项目学习其架构和WebSocket的应用。

以太坊网络状态平台EthStats

以太坊网络状态(EthStats)是用于跟踪以太坊网络状态的可视化平台,使用WebSockets技术从正在运行中的节点中接收统计信息并通过不同的规则来输出数据,用户可以通过该网站直观的了解以太坊节点的运行情况。

声明:下面的内容写于2018年,因此界面跟现在可能有差异,但架构及技术本身还是有学习意义。

Ethstats监控节点信息(节点名称、节点运行环境、运行时间、算力情况、Peer数量、Pending交易数量、最新区块号和HASH、总难度、区块交易)、网络平均HASHRATE、Gas Limit、活跃节点数量、叔区块数据都可以通该网站实时获取。本文介绍整个平台的实现方案,能够为我们提供一些实现参考,进而加深对WebSocket技术的了解。

EthStats

整体结构

EthStats架构

平台涉及两个项目

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有很多相似的地方。

  • 相同点

    1. 都是一样基于TCP的,都是可靠性传输协议。
    2. 都是应用层协议。
  • 不同点

    1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
    2. 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架构

WebSocket兼容性

随着HTML5的普及,现代浏览器(IE10+)基本上都已经原生支持WebSocket了,下面是支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>

总结

区块链项目开源的属性,让对技术有着好奇的我不会放过学习的机会。