Bitshares 开发者系列(2)—— Bitsharesjs-ws 库源码解读

in #bitshares6 years ago

官方接口访问库 GitHub : https://github.com/bitshares/bitsharesjs-ws
在阅读源码之前,最好先浏览一遍 Bitshares 官方公开的开发文档,虽然文档中对开发细节描述的不是很清楚,但也能对系统的通讯方式有个详细的了解。

项目库介绍


bitsharesjs-ws 是比特股的 Github 官方账号创建的一个项目,主要功能是提供一个访问比特股 API 节点的工具,与 API 节点通讯访问方式采用的是 WebSocket 方式,代码采用JS编写,可以同时运行于浏览器端与Node环境下。

项目代码结构如下:

+ build         * 编译版本的项目文件
+ examples      * 项目库的使用案例      
+ lib           * 项目库源码文件
+ test          * 项目单元测试
+ tools         * 编译工具

直接进入 lib 目录,打开 index.js 文件阅读源码,你会发现项目库共导出三个如下模块:

export {
    Apis,           //  工具库的主要接口,对外公开了 API 的访问方式,与节点服务器的通讯全部依赖此模块的内部实现
    ChainConfig,    //  Bitshares 区块链公开的参数配置项
    Manager         //  与节点服务器之间的连接管理器,负责管理与检查和节点服务器之间的连接状态
}

Apis 模块实现逻辑


Apis 模块对应的实现文件是 ApisInstances.js,该模块引入了 ChainWebSocket.jsGrapheneApi.js 等内部模块,这些内部子模块的作用稍后再作描述,先看Apis模块导出的供外部系统调用的方法:

export default {
  /**
   * String   cs              代表要连接的完整钱包节点地址。
   * Boolean  connect         创建成功之后是否直接执行创建的对象的 connect 方法。
   * int      connectTimeout  连接节点时,响应超时的时间。
   * Boolean  enableCrypto    是否进行加密通讯。
   *
   * @return  该方法会返回一个 ApiInstance 的实例对象,全局共享此单例对象。
   */
  instance (cs = "ws://localhost:8090", connect, connectTimeout = 4000, enableCrypto)

  /**
   * Function   callback  设置一个当网络连接状态发生改变的时候的回调函数。
   */
  setRpcConnectionStatusCallback (callback)

  /**
   * 设定当网络连接失败的时候,是否自动重连,会重新设置 autoReconnect 的值。
   */
  setAutoReconnect (auto)

  /**
   * 重置当前的连接,假如当前是连接状态,会直接
   */
  reset (cs = "ws://localhost:8090", connect, connectTimeout = 4000)
  
  /**
   * 获取当前连接的服务器节点的区块链 ID 
   */
  chainId ()
  
  /**
   * 关闭当前的连接  
   */
  close ()
}

查看上面导出的方法实现,可以发现 instance 方法会构建一个 ApisInstance 对象的实例,并让外部系统该通过此实例调用对应类别的API接口,关于 ApisInstance 对象的定义如下:

Class ApiInstance {
    chain_id    // 当前连接的区块链 id
    String url  // 当前连接的完整钱包节点地址。
    statusCb
    ChainWebSocket ws_rpc // 创建的 ChainWebSocket 对象实例。    
    Promise init_promise
    GrapheneApi _db
    GrapheneApi _net
    GrapheneApi _hist
    GrapheneApi _crypt


    /**
     * 调用 instance() 的时候,第二个参数传递 true,会自动调用此函数连接区块节点,旨在初始化网络连接。
     * 并且初始化 inst 内部的各个关键属性,init_promise, _db, ws_rpc ...
     */
    connect (cs, connectTimeout, enableCrypto = false)
    close()
    db_api()
    network_api()
    history_api()
    crypto_api()
    setRpcConnectionStatusCallback()
}

此处方法的功能是与 Apis 模块提供的那些同名方法是相同的,因为 Apis 模块公开的那些方法,也是最终的中转到了此处的方法实现。重点需要注意的就是此对象的内部属性,主要有ws_rpcinit_promise_db_net_hist_crypt等。

  • ws_rpc 此属性的值是一个 ChainWebSocket 对象的实例,内部包装了 ReconnectWebSocket 对象,并定义了一套API接口的调用流程。最终由此对象向服务器节点发起 API 调用,并处理返回结果,这样一个完整的访问流程,就是此内部模块定义的。

  • init_promise 一个Promise对象,因为网络接口调用、WebSocket连接等操作都是异步响应的,所以此处定义了一个 Promise 对象来给外部系统使用,当与服务器节点之间的连接全部初始化完成之后,此异步对象就会变为完成状态。

  • _db _net _hist _crypt 这些属性都分别是一个 GrapheneApi 对象的实例,用于区分不同类别的API调用,因为 Bitshares 系统为不同的 API 接口设置有不同的 API 访问令牌,所以单独为每个 API 类型都创建了一个实例,以便外部系统调用不同类型的 API 的时候,不用反复设置 API 令牌标识。

关于 ChainWebSocket 与 ReconnectWebSocket 应该进一步解释下它们的作用,首先说一下 ReconnectWebSocket, ReconnectWebSocket 实现了在浏览器环境下的断线重连机制,因为整个模块是使用 WbeSocket 与服务节点进行通信连接的,所以可能会偶尔的发生 WebSocket 断开连接的问题。

因为当断开连接以后,可能就会导致订阅通知与API调用接口不能正常使用,这就需要外部系统自己再定义一套复杂的连接状态检查规则,来进行断线之后的连接与初始化操作。所以索性就在模块内部实现了一个 ReconnectWebSocket 模块,实现一套 Bitshares 系统专用的断线重连的制度,来保证与服务器节点连接的稳定性,减少外部系统的复杂程度,让外部系统能专心于业务逻辑的开发。

具体是如何定义重连制度的,可以自己查看 ReconnectWebSocket 模块的源码,这里不再详细介绍,但其实官方引用的 ReconnectWebSocket 在实现的时候也有一个问题,ReconnectWebSocket 仅仅是包装了 WebSocket 对象,并在内部依赖 document 与 window 对象,所以导致断线重连的制度在Nodejs的环境中是不可用的,只有在浏览器的环境下才会有断线重连保护。

ChainWebSocket 是建立在可靠的 ReconnectWebSocket 连接之上的,然后针对 Bitshares 系统的特性,构建了一个API的访问流程,和订阅通知的处理流程。外部系统的所有API操作操作都会经由 ChainWebSocket 处理并通过 WebSocket 发送到服务器节点,然后在响应结果发生以后,再由 ChainWebSocket 解析并转交给外外部系统处理。

由于在 Nodejs 的环境中是不存在 WebSocket 对象的,所以引入了一个开源的第三方 WebSocket 的实现 —— ws:

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 连接操作,告诉模块要到的服务器节点,并且配置是否开启断线重连制度。

然后通过返回的 instance 对象属性 init_promise 来指定连接成功之后的下一步操作。

假如你在连接成功之后通过 instance.__db 属性调用了 get_account API,那么你的请求会经由 GrapheneApi 打上对应的API令牌标识,再投递给 ChainWebSocket 发送请求,当响应结果到来的时候,又会回调你的处理函数来完成一个完整的API请求流程。

Manager 模块实现逻辑


辅助 Apis 模块实现 WebSocket 连接的管理,更详细的功能不再描述,可以自行查看源码。

Manager 模块实现逻辑


定义了一些 Bitshares 链上的一些属性配置,我目前对 Bitshares 系统的其他细节还不了解,就不作解读说明了,等以后学习了更多的知识,再补充这些内容供大家参考。

总结


bitsharesjs-ws 针对 Bitshares 系统,定义了一套 API 调用的流程,并拓展了 WebSocket 的功能来维持一个稳定的连接通道,让外部系统能够专心于业务 API 的开发,但在其模块职责领域上,还应该有很多的可拓展空间,在此感谢此模块开发者为我们提供了一个好用的工具库,并节省了我们宝贵的时间。

Sort:  

Congratulations @bts500, this post is the fifth most rewarded post (based on pending payouts) in the last 12 hours written by a Dust account holder (accounts that hold between 0 and 0.01 Mega Vests). The total number of posts by Dust account holders during this period was 13369 and the total pending payments to posts in this category was $1439.37. To see the full list of highest paid posts across all accounts categories, click here.

If you do not wish to receive these messages in future, please reply stop to this comment.

Congratulations @bts500! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

Click here to view your Board

Vote for @Steemitboard as a witness and get one more award and increased upvotes!

Congratulations @bts500! You received a personal award!

Happy Steem Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

Downvote challenge - Add up to 3 funny badges to your board
Use your witness votes and get the Community Badge
Vote for @Steemitboard as a witness to get one more award and increased upvotes!

Coin Marketplace

STEEM 0.30
TRX 0.11
JST 0.033
BTC 64243.42
ETH 3152.93
USDT 1.00
SBD 4.28