多节点组网概述

在之前的课程内容中,我们的关注点集中在单一节点上的状态机逻辑实现上,这是因为 状态机复制的问题,是由tendermint负责完成的:我们在任何一个节点提交的交易请求, tendermint可以透明地帮我们在多个节点间实现同步。

在这一章,我们将通过前面已经学习过的简单的ABCI计数应用来学习如何部署多个tendermint节点, 并进一步理解tendermint共识的建立过程。为了简化操作,我们将首先了解如何将ABCI 应用与tendermint库集成为单一可执行文件,然后利用这个单一完整的程序来部署4个 节点旳区块链:

在tendermint节点组成的区块链中有两种节点:验证节点和观察节点,只有验证节点 参与共识的建立。在这一章,我们将同时学习如何部署验证节点和观察节点。

tendermint是拜占庭容错的共识机制,因此在3f+1个验证节点中,允许不超过f个节点 出现拜占庭错误。在活性与可靠性中tendermint选择了可靠性,因此当3f+1个节点中 超过f个节点出现拜占庭错误后,整个系统将停机,我们也将通过实验观察到这一点。

实现abci接口

使用go语言开发tendermint链上应用有一个额外的优势,就是可以将tendermint节点 与ABCI应用集成在单一程序内发布。

我们还是使用计数状态机作为ABCI应用示例,在多个节点之间维护单一的计数值:

为简化问题,我们仅实现三个主要的ABCI接口 —— CheckTx、DeliverTx和Query, 同时,使用0x01~0x03分别表示递增、递减和复位交易:

type App struct {types.BaseApplicationValue int
}func (app *App) CheckTx(tx []byte) (rsp types.ResponseCheckTx) {if tx[0] < 0x04 {  return }rsp.Code = 1return
}func (app *App) DeliverTx(tx []byte) (rsp types.ResponseDeliverTx) {switch tx[0] {case 0x01 : app.Value += 1case 0x02 : app.Value -= 1case 0x03 : app.Value = 0}return
}func (app *App) Query(req types.RequestQuery) (rsp types.ResponseQuery) {rsp.Log = fmt.Sprintf("counter: %d",app.Value)return
}

你可以查看~/repo/go/src/hubwiz.com/c8/smr.go中的完整示例代码。

命令行封装:cobra子命令

Tendermint的命令行程序是基于spf13/cobra开发包开发的,每个子命令都对应着 一个cobra/Command结构的实例,例如,初始化节点子命令init对应着InitFilesCmd这个 对象,而RootCmd则对应着根命令:

因此我们可以利用这些封装好的cobra命令,非常快速地生成一个具有节点同样功能 的命令行程序:

func main(){root := commands.RootCmdroot.AddCommand(commands.InitFilesCmd) root.AddCommand(commands.ResetAllCmd)exec := cli.PrepareBaseCmd(root,"WIZ",".")exec.Execute()
}

PrepareBaseCmd()方法将指定的Command实例转换为一个Executor对象,它进行 必要的环境变量设置(根据第二个参数指定的前缀),并确定命令行的默认工作目录 (第三个参数)。

在上面代码中,我们将默认工作目录设置为当前目录,这意味着在默认情况下, 当执行init子命令时,将在当前目录下生成data和config子目录。不过在RootCmd 中定义了全局标志–home,可以在命令行指定其他目录为节点工作目录。例如, 下面的代码将在当前目录下的n1子目录中执行初始化命令:

~/repo/go/src/hubwiz.com/c8$ go run smr.go init –home n1

实现节点提供器

与其他cobra命令不同,用于启动节点旳node子命令对应于一个NewRunNodeCmd()方法, 它是一个构建cobra/Command实例的工厂方法,需要传入一个NodeProvider类型的变量:

NodeProvider其实是返回节点实例的函数,其原型定义如下:

type NodeProvider func(config *cfg.Config, logger log.Logger) (*node.Node, error)

其中,参数config(节点配置)和logger(日志)都是由tendermint框架在启动时传入的参数。

NodeProvider是基于具体的abci应用创建的。例如,下面的代码利用abci应用实例创建一个 NodeProvider实例,并最终得到cobra/Command实例添加到根命令中:

app := NewApp()
nodeProvider := makeNodeProvider(app)
root.AddCommand(commands.NewRunNodeCmd(nodeProvider))

makeNodeProvider()函数返回NodeProvider实例,其实现代码如下:

func makeNodeProvider(app types.Application) node.NodeProvider {return func(config *cfg.Config, logger log.Logger) (*node.Node, error) {nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())if err != nil {  return nil, err }return node.NewNode(config,privval.LoadOrGenFilePV(config.PrivValidatorFile()),nodeKey,proxy.NewLocalClientCreator(app),node.DefaultGenesisDocProviderFunc(config),node.DefaultDBProvider,node.DefaultMetricsProvider(config.Instrumentation),logger)}
}

编译安装

在整合了abci应用和cobra命令代码后,为了简化后续的键盘输入,我们将其 编译安装:

~/repo/go/src/hubwiz.com/c8$ go install smr.go

现在可以很简单地执行节点程序:

~/repo/go/hubwiz.com/c8$ smr

单节点实验

Tendermint支持两种类型的节点:验证节点(Validator)和观察节点(Observer)。显然,对于 网络中的第一个节点而言,必须是一个验证节点 —— 否则没有办法出块了:

第一个节点旳基本参数是:

P2P通信端口:26656
RPC服务端口:26657
节点目录:n1
节点初始化与启动

进入1#终端,执行init子命令初始化第一个节点,注意使用–home标志指定节点目录:

~/repo/go/src/hubwiz.com/c8$ smr init –home n1

执行show_node_id子命令显示并记录第一个节点旳ID:

~/repo/go/src/hubwiz.com/c8$ smr show_node_id –home n1

输出结果为节点旳ID,我们后续会用到,请记下来(你的ID应该与此不同):

fd8debec2b97adfa0f6e8bae939c22a69cda9741

然后执行node子命令启动第一个节点:

~/repo/go/src/hubwiz.com/c8$ smr node –home n1

测试状态机交易

进入2#终端,执行如下交易并分别查询:

增加:

~$ curl localhost:26657/broadcast_tx_commit?tx=0x0101
~$ curl localhost:26657/abci_query

减少:

~$ curl localhost:26657/broadcast_tx_commit?tx=0x0201
~$ curl localhost:26657/abci_query

复位:

~$ curl localhost:26657/broadcast_tx_commit?tx=0x0301
~$ curl localhost:26657/abci_query

添加观察节点

现在我们在网络中添加一个观察节点,它不会参与到共识的建立:

第二个节点旳基本参数是:

P2P通信端口:36656
RPC服务端口:36657
节点目录:n2
节点初始化配置与启动

进入2#终端,执行init子命令初始化第二个节点:

~/repo/go/src/hubwiz.com/c8$ smr init –home n2

由于这个节点是观察节点,因此我们可以直接使用验证节点旳创世文件:

~/repo/go/src/hubwiz.com/c8$ cp n1/config/genesis.json n2/config/genesis.json

同时修改n2/config/config.toml,设置其rpc监听端口为36657,p2p监听端口为36656, 并使其主动连接第一个节点,其中fd8d…41为第一个节点旳ID(使用show_node_id 子命令获取):

[rpc]
laddr = “tcp://0.0.0.0:36657”[p2p]
laddr = “tcp://0.0.0.0:36656”
persistent_peers = “fd8debec2b97adfa0f6e8bae939c22a69cda9741@127.0.0.1:26656”

现在2#终端启动第二个节点:

~/repo/go/src/hubwiz.com/c8$ smr node –home n2
交易测试

无论利用哪个节点提交交易,都可以保证所有的节点状态同步。

首先在5#终端分别查看节点一和节点二的状态:

~/repo/go/src/hubwiz.com/c8$ curl localhost:26657/abci_query
~/repo/go/src/hubwiz.com/c8$ curl localhost:36657/abci_query

然后利用节点一的rpc接口提交交易:

~/repo/go/src/hubwiz.com/c8$ curl localhost:26657/broadcast_tx_commit?tx=0x01
再次查看节点一和节点二的状态,你可以观察到同样的最新状态

停机测试

由于我们只有一个验证节点,显然当该节点停机时,整个集群将无法达成共识。

首先在1#终端按ctrl+c停止smr的运行,然后在5#终端通过节点二提交交易:

~/repo/go/src/hubwiz.com/c8$ curl localhost:26657/broadcast_tx_commit?tx=0x01
该命令会一直挂起直至超时,因为交易始终无法确认。

添加验证节点

现在我们继续向网络中添加第三个节点,这次是一个验证节点:

第三个节点旳基本参数是:

P2P通信端口:46656
RPC服务端口:46657
节点目录:n3
节点初始化配置与启动

进入3#终端,执行init子命令初始化节点三的目录n3:

~/repo/go/src/hubwiz.com/c8$ smr init –home n3
由于这个节点是验证节点,因此我们需要在节点一现有的创世文件中添加该节点 的priv_validator.json文件中的公钥和地址,其中每个验证节点旳power值用来 表征其代表的权益:

在上面的配置中,由于每个节点旳power都是10,因此每个节点都有50%(10/(10+10)) 的机率出块。

然后将新的创世文件分发给节点二和节点三:

~/repo/go/src/hubwiz.com/c8$ cp n1/config/genesis.json n2/config/genesis.json
~/repo/go/src/hubwiz.com/c8$ cp n1/config/genesis.json n3/config/genesis.json

同时修改n3/config/config.toml,设置其rpc监听端口为46657,p2p监听端口为46656, 并使其主动连接第一个节点,其中fd8d…41为第一个节点旳ID(使用show_node_id 子命令获取):

[rpc]
laddr = “tcp://0.0.0.0:46657”[p2p]
laddr = “tcp://0.0.0.0:46656”
persistent_peers = “fd8debec2b97adfa0f6e8bae939c22a69cda9741@127.0.0.1:26656”

现在3#终端启动第二个节点:

~/repo/go/src/hubwiz.com/c8$ smr node –home n3
交易测试

无论利用哪个节点提交交易,都可以保证所有的节点状态同步。

首先在5#终端分别查看三个节点的状态:

~/repo/go/src/hubwiz.com/c8$ curl localhost:26657/abci_query
~/repo/go/src/hubwiz.com/c8$ curl localhost:36657/abci_query

然后利用节点一的rpc接口提交交易:

~/repo/go/src/hubwiz.com/c8$ curl localhost:26657/broadcast_tx_commit?tx=0x0101
再次查看三个节点的状态,你可以观察到同样的最新状态

停机测试

由于3f+1个验证节点中,才允许f个发生拜占庭故障,因此两个验证节点旳任一个 出现故障,整个集群都将停止共识。

首先在3#终端按ctrl+c停止smr的运行,然后在5#终端通过节点二提交交易:

~/repo/go/src/hubwiz.com/c8$ curl localhost:36657/broadcast_tx_commit?tx=0x0102
该命令会一直挂起直至超时,因为交易始终无法确认。

但是观测节点旳停机不会影响共识的达成。

拜占庭容错实验

现在我们添加第四个节点,并将全部节点都设置为验证节点,来测试当其中某个 节点故障时,系统的容错能力:

第四个节点旳基本参数是:

P2P通信端口:56656
RPC服务端口:56657
节点目录:n4
节点初始化配置与启动

进入1#终端,首先初始化第四个节点目录:

~/repo/go/src/hubwiz.com/c8$ smr init –home n4
修改节点一的创世文件,将其他三个节点的priv_validator.json文件中的公钥和地址 添加进去,然后将新的创世文件分发给其他节点

~/repo/go/src/hubwiz.com/c8$ cp n1/config/genesis.json n2/config/genesis.json
~/repo/go/src/hubwiz.com/c8$ cp n1/config/genesis.json n3/config/genesis.json
~/repo/go/src/hubwiz.com/c8$ cp n1/config/genesis.json n4/config/genesis.json

同时修改节点2/3/4的config/config.toml,设置其rpc监听端口分别为36657/46657/56657, p2p监听端口分别为36656/46656/56656,并使其主动连接第一个节点,其中fd8d…41为第一 个节点旳ID(使用show_node_id子命令获取)。以节点2为例:

[rpc]
laddr = “tcp://0.0.0.0:56657”[p2p]
laddr = “tcp://0.0.0.0:56656”
persistent_peers = “fd8debec2b97adfa0f6e8bae939c22a69cda9741@127.0.0.1:26656”

分别在四个终端启动四个目录对应的节点,例如,在2#终端启动节点2:

~/repo/go/src/hubwiz.com/c8$ smr node –home n2
容错测试

由于3f+1个验证节点中,允许f个发生拜占庭故障,因此我们的四个节点旳小集群, 允许任一个出现拜占庭错误。

首先在1#终端按ctrl+c停止smr的运行,然后在5#终端通过节点二提交交易:

~/repo/go/src/hubwiz.com/c8$ curl localhost:36657/broadcast_tx_commit?tx=0x0101
可以观察到交易正常完成。

8.tendermint多节点组网相关推荐

  1. 【刘文彬】EOS多节点组网:商业场景分析以及节点启动时序

    原文链接: https://www.cnblogs.com/Evsward/p/eos-boot.html 区块链公链都是基于p2p网络,本篇文章将建立一个多节点不同职责参与的EOS的测试网络,根据路 ...

  2. ZigBee多节点无线物联自组网设计

    目前,针对一定区域内信息和设备进行监测和控制多采用有线方式通信,网络在布线和维护过程中由于线路腐蚀和损坏造成网络中断,给正常生产工作带来影响,同时也增加了人力资源成本比重.基于此,从节省成本和网络稳定 ...

  3. 计算机网络 - NB-IOT/LoRa/Zigbee 无线组网方案对比

    NB-IOT/LoRa/Zigbee无线组网方案对比 NB-IOT/LoRa/Zigbee无线组网方案对比 NB-IOT/LoRa/Zigbee无线组网方案对比 物联网设备节点组网存在2种组网方式, ...

  4. 组一个包括协调器路由器终端节点的zigbee网络,通过串口能获取它们的拓扑结构。

    组一个包括协调器路由器终端节点的zigbee网络,通过串口能获取它们的拓扑结构. 实验简介 实验环境 实验准备 1.下载基本工程文件 2.打开工程文件 3.尝试编译 路由器和终端节点 查看完整原文 旧 ...

  5. Zigbee物联网组网

    物联网的核心和基础是互联网,物联网是在互联网基础上的延伸和扩展的网络,然而在物联网当中基于海量数据的无线传感网是物联网极具代表的网络之一,其用户端延伸和扩展到了任何物品与物品之间,进行信息交换和通信. ...

  6. 41、【华为HCIE-Storage】--Oceanstor9000 组网规划

    ------------------------------------重要说明------------------------------------ 以下部分内容来网络,部分自华为存储官方教材 具 ...

  7. xsd 节点可循环_应用案例丨邦纳无线振动和温度监测解决方案在冷却循环水系统中的应用...

    点击关注▲ "邦纳",开启智造之旅 冷却循环水系统在工业生产中发挥着重要的作用,主要使用水作为冷却介质,进行循环冷却.水泵电机是该系统不可或缺的重要组成部分.电机是否正常运转直接影 ...

  8. zigbee-无线点灯-协调器节点

    开发环境:IAR 8.10 Z-stack 2.5 功能:协调器与终端节点组网,实现点对点通信.终端向协调器发送"D1",协调器LED灯闪烁. 流程图: 具体代码: 定义所需要的变 ...

  9. 一文看懂Mesh组网

    一.Mesh组网初了解 1.Mesh组网是什么? Mesh是一种多节点.无中心.自组织的无线多跳通信网络(注:当前也有部分厂家及应用市场引出了有线Mesh及混合互联:即有线+无线的概念,但我们这里主要 ...

  10. 边缘节点的需求分析和核心技术研究

    本文分析了来自5G.IoT 和CDN的边缘计算需求,对边缘计算的总体架构进行了研究.边缘节点作为数据中心网络中的一个重要组成部分,边缘硬件.边云协同和边边协同的技术发展将会非常重要.未来,需要解决好技 ...

最新文章

  1. MySQL中authorization_IdentitiServser4 + Mysql实现Authorization Server
  2. 深入理解 Java 注解
  3. java卡安全域_java – 在安全管理器下解析许多域后,程序内存不足?
  4. java编程思想泛型对混入的详细探讨
  5. anaconda如何更改环境配置_手把手教新手安装Anaconda配置开发环境
  6. Linux基础-固化命令的方式grep
  7. js之prototype、__proto__与constructor(图解)以及原型链
  8. 2021年12月最新大数据白皮书(附下载)
  9. 易点易动助力企业年中固定资产盘点
  10. Linux系统下如何复制粘贴文件(待更新)
  11. cad插入块_CAD中创建块与写块的区别总结
  12. github上传本地项目代码
  13. 根据经纬度获取精确地址 (百度地图)
  14. Normalize.css简书
  15. 亚当斯分区曝光法俗解之一
  16. Centos6下Redis学习(一)——Java客户端Lettuce的使用、Springboot整合
  17. 〔王鹰教程五〕和弦的分类记忆法
  18. 求电缆最小长度——最小生成树
  19. [裴礼文数学分析中的典型问题与方法习题参考解答]4.5.1
  20. 检测ip是否为中国php,PHP判断IP是中国IP还是外国IP

热门文章

  1. 风险评估-HEAVENS
  2. @永和:为自己编码 --- 开源中国众包平台上线
  3. 集群资源调度系统设计架构总结
  4. 用Netty实现WebSocket网络聊天室
  5. pentaho8.1安装
  6. mb.php js 劫持,黑帽seo防止网站被k的js劫持跳转代码
  7. 树莓派pythongpio编程_基于树莓派的python GPIO编程-常用函数综合整理
  8. java 麻将小程序_麻将小程序麻将这么玩
  9. element plus 部分组件转英文问题
  10. 如何查看电脑CPU温度,笔记本温度显示怎么开启