作者:freewind
比原项目仓库:https://github.com/Bytom/bytom
最开始我对于这个问题一直有个疑惑:区块链是一个分布式的网络,那么一个节点启动后,它怎么知道去×××别的节点从而加入网络呢?

看到代码之后,我才明白,原来在代码中硬编码了一些种子地址,这样在启动的时候,可以先通过种子地址加入网络。虽然整个网络是分布式的,但是最开始还是需要一定的中心化。

预编码内容

对于配置文件config.toml,比原的代码中硬编码了配置文件内容:

config/toml.go#L22-L45

var defaultConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
fast_sync = true
db_backend = "leveldb"
api_addr = "0.0.0.0:9888"
`var mainNetConfigTmpl = `chain_id = "mainnet"
[p2p]
laddr = "tcp://0.0.0.0:46657"
seeds = "45.79.213.28:46657,198.74.61.131:46657,212.111.41.245:46657,47.100.214.154:46657,47.100.109.199:46657,47.100.105.165:46657"
`var testNetConfigTmpl = `chain_id = "testnet"
[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = "47.96.42.1:46656,172.104.224.219:46656,45.118.132.164:46656"
`var soloNetConfigTmpl = `chain_id = "solonet"
[p2p]
laddr = "tcp://0.0.0.0:46658"
seeds = ""
`

可以看出,对于不同的chain_id,预设的种子是不同的。

当然,如果我们自己知道某些节点的地址,也可以在初始化生成config.toml后,手动修改该文件添加进去。

启动syncManager

那么,比原在代码中是使用这些种子地址并连接它们的呢?关键在于,连接的代码位于SyncManager中,所以我们要找到启动syncManager的地方。

首先,当我们使用bytomd node启动后,下面的函数将被调用:

cmd/bytomd/commands/run_node.go#L41

func runNode(cmd *cobra.Command, args []string) error {// Create & start noden := node.NewNode(config)if _, err := n.Start(); err != nil {// ...}// ...
}

这里调用了n.Start,其中的Start方法,来自于Node所嵌入的cmn.BaseService

node/node.go#L39

type Node struct {cmn.BaseService// ...
}

所以n.Start对应的是下面这个方法:

vendor/github.com/tendermint/tmlibs/common/service.go#L97

func (bs *BaseService) Start() (bool, error) {// ...err := bs.impl.OnStart()// ...
}

在这里,由于bs.impl对应于Node,所以将继续调用Node.OnStart():

node/node.go#L169

func (n *Node) OnStart() error {// ...n.syncManager.Start()// ...
}

可以看到,我们终于走到了调用了syncManager.Start()的地方。

syncManager中的处理

然后就是在syncManager内部的一些处理了。

它主要是除了从config.toml中取得种子节点外,还需要把以前连接过并保存在本地的AddressBook.json中的节点也拿出来连接,这样就算预设的种子节点失败了,也还是有可能连接上网络(部分解决了前面提到的中心化的担忧)。

syncManager.Start()对应于:

netsync/handle.go#L141

func (sm *SyncManager) Start() {go sm.netStart()// ...
}

其中sm.netStart(),对应于:

netsync/handle.go#L121

func (sm *SyncManager) netStart() error {// ...// If seeds exist, add them to the address book and dial outif sm.config.P2P.Seeds != "" {// dial outseeds := strings.Split(sm.config.P2P.Seeds, ",")if err := sm.DialSeeds(seeds); err != nil {return err}}// ...
}

其中的sm.config.P2P.Seeds就对应于config.toml中的seeds。关于这两者是怎么对应起来的,会在后面文章中详解。

紧接着,再通过sm.DialSeeds(seeds)去连接这些seed,这个方法对应的代码位于:

netsync/handle.go#L229

func (sm *SyncManager) DialSeeds(seeds []string) error {return sm.sw.DialSeeds(sm.addrBook, seeds)
}

其实是是调用了sm.sw.DialSeeds,而sm.sw是指Switch。这时可以看到,有一个叫addrBook的东西参与了进来,它保存了该结点之前成功连接过的节点地址,我们这里暂不多做讨论。

Switch.DialSeeds对应于:

p2p/switch.go#L311

func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error {// ...perm := rand.Perm(len(netAddrs))for i := 0; i < len(perm)/2; i++ {j := perm[i]sw.dialSeed(netAddrs[j])}// ...
}

这里引入了随机数,是为了将发起连接的顺序打乱,这样可以让每个种子都获得公平的连接机会。

sw.dialSeed(netAddrs[j])对应于:

p2p/switch.go#L342

func (sw *Switch) dialSeed(addr *NetAddress) {peer, err := sw.DialPeerWithAddress(addr, false)// ...
}

sw.DialPeerWithAddress(addr, false)又对应于:

p2p/switch.go#L351

func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (*Peer, error) {// ...log.WithField("address", addr).Info("Dialing peer")peer, err := newOutboundPeerWithConfig(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, sw.peerConfig)// ...
}

其中的persistent参数如果是true的话,表明这个peer比较重要,在某些情况下如果断开连接后,还会尝试重连。如果persistentfalse的,就没有这个待遇。

newOutboundPeerWithConfig对应于:

p2p/peer.go#L69

func newOutboundPeerWithConfig(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {conn, err := dial(addr, config)// ...
}

继续dial,加入了超时:

p2p/peer.go#L284

func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) {conn, err := addr.DialTimeout(config.DialTimeout * time.Second)if err != nil {return nil, err}return conn, nil
}

addr.DialTimeout对应于:

p2p/netaddress.go#L141

func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) {conn, err := net.DialTimeout("tcp", na.String(), timeout)if err != nil {return nil, err}return conn, nil
}

终于到了net包的调用,开始真正去连接这个种子节点了,到这里,我们可以认为这个问题解决了。

转载于:https://blog.51cto.com/13794581/2125718

【比原链】比原启动后去哪里连接别的节点相关推荐

  1. 一场AMA讲透比原链BaaS平台Bystack

    这是今年以来,ChainNode链节点举办的最火爆的一场AMA. 3位嘉宾解答,近200名用户参与互动,留言数410余条,问答总字数超2.8万字,3小时浏览量近5.1万. 5月22日晚,长铗(巴比特. ...

  2. 2019比原链全球开发者大会落幕:高举开源旗帜,聚焦区块链应用落地

    北京时间8月25日,2019比原链全球开发者大会在美国旧金山Fort Mason Cowell Theater成功举办.会议吸引了来自美国.俄罗斯.印度.比利时.加拿大.巴西.赞比亚.委内瑞拉等多个国 ...

  3. 干货|读懂公链学开发:深入浅出剖析比原链技术特性(分享实录)

    大家好,非常感谢大家在百忙之中抽空收听比原链技术入门课程,我是比原链技术运营经理钟立飞. 今天主要给大家介绍一些比原链的基础技术知识,希望能给大家带来一些启发.同时比原链的开发大赛也在进行当中,欢迎大 ...

  4. 【比原链起步】在服务器上搭建自己的Bytom节点

    此文将投稿给"比原链|用技术书写未来". 前言:作为一个2016年末开始投资区块链的Python程序员,我对比原链从诞生发布到主网上线的过程是很有记忆的,公司新的业务是比原链挖矿和 ...

  5. 区块链传奇之国内三大公链:NEO小蚁、Qtum量子链、BTM比原链简介

    刚进链圈,听朋友谈起,说国内有三大公链是大家比较关注的,分别是:NEO小蚁.Qtum量子链.BTM比原链,这三个公链,最近发展如何?我们一起看看! 什么是公共区块链(公链)? 公有区块链(Public ...

  6. Docker 安装启动后无法连接服务器

    Docker 安装启动后服务无法连接: 原因:docker0 ip 与 系统ip冲突 # 查看ip ifconfig 解决方法重新绑定docker0 ip(docker0 不能设置为localhost ...

  7. 解读Bytom丨比原链BTM,连接两个世界的无限可能

    物理(原子)世界中的资源能发挥出的价值极其有限,如果原子世界中的资源能够上链到比特世界中,并且可以在两个世界中进行交易流通,人们必将从中获利.比原链(Bytom)就是连接比特(byte)世界和原子(a ...

  8. 浅析Facebook LibraBFT与比原链Bystack BBFT共识

    如果说什么是区块链的灵魂,那一定是共识机制. 它是区块链的根基.无论公链或是联盟链,共识机制都从基础上限制了区块链的交易处理能力和扩展性. 2019年6月18日,Facebook 发布了自己 Libr ...

  9. 原链YCC战略定位:公链+私链(联盟链、私有链),实现价值传递

    作为一种分布式的记账方式,区块链的重要特征就是去中心化.不可篡改.基于机器信任的原则,利用区块链技术可以使没有信任关系的用户之前完成无风险交易.因此,它可以记录每个用户不可篡改的信息或交易记录,比如个 ...

最新文章

  1. 新算法可模拟人脑整体神经电路
  2. 在linux系统 挂载光盘:mount时提示: you must specify the filesystem type
  3. JS一起学02:函数传参、操作属性第二种方式、提取行间事件、循环、this、焦点问题、联动选择、选项卡焦点图
  4. OpenGL延迟着色之一
  5. spring与cxf的整合
  6. Possible missing firmware /lib/firmware/i915/bxt_guc_ver8_7.bin for module i915
  7. Flow monitoring in Software-Defined Networks
  8. 百度SEO站群小旋风蜘蛛池站群X8模板
  9. 游戏建模用什么软件,学游戏建模哪里好?
  10. ES6-let 和 const 命令
  11. C++数据结构与算法 队列的应用之图元识别
  12. 第F题 真约束之和(通解)古希腊数学家毕达哥拉斯在自然数研究中发现,220的所有真约数(即不是自身的约数)之和为:  1+2+4+5+10+11+20+22+44+55+110=284
  13. 集线器、交换机和路由器之间的区别
  14. 教你屏蔽CSDN广告
  15. 一文读懂什么是数据产品交易
  16. PyCharm设置背景颜色为白色
  17. web服务器与APP服务器
  18. Ansible动态Inventory格式
  19. 阿昆同学的Java学习日记Day1
  20. 5行代码提升时间序列预测,都有用!

热门文章

  1. python强制退出循环_for循环、while循环、break跳出循环、continue结束本次循环、exit退出本次脚本...
  2. 做软件测试却不知道这些测试工具?利用好可以涨薪50%
  3. 面试最后,HR 最后会说“我的问题问完了,你有什么要问我的吗?”如何理解和回答这句话?
  4. antd树型选择控件选择父级_element的tree树形菜单回显、父级半勾选
  5. zookeeper版本更新_很遗憾,没有一篇文章能讲清楚ZooKeeper
  6. 关于annotation object的旋转
  7. Java求1到任意一个数的阶乘和
  8. gpu超算算法_英伟达推GPU加速Arm服务器参考设计!微软Azure启动GPU超算实例
  9. C++输出倒三角加数字
  10. 零基础入门语义分割-Task5 模型训练与验证