剥开比原看代码(二):比原启动后去哪里连接别的节点

news/2024/12/21 22:55:35/

作者:freewind

比原项目仓库:

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockchain/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包的调用,开始真正去连接这个种子节点了,到这里,我们可以认为这个问题解决了。


http://www.ppmy.cn/news/485448.html

相关文章

剥开比原看代码(六):比原是如何把请求区块数据的信息发出去的

作者&#xff1a;freewind 比原项目仓库&#xff1a; Github地址&#xff1a;https://github.com/Bytom/bytom Gitee地址&#xff1a;https://gitee.com/BytomBlockchain/bytom 在前一篇中&#xff0c;我们说到&#xff0c;当比原向其它节点请求区块数据时&#xff0c;BlockKee…

剥开比原看代码06:比原是如何把请求区块数据的信息发出去的

作者&#xff1a;freewind 比原项目仓库&#xff1a; Github地址&#xff1a;https://github.com/Bytom/bytom Gitee地址&#xff1a;https://gitee.com/BytomBlockchain/bytom 在前一篇中&#xff0c;我们说到&#xff0c;当比原向其它节点请求区块数据时&#xff0c;BlockKee…

vue中使用raphael.js实现地图绘制

一、效果图 二、在vue中引入raphael.js npm i raphael -S 三、封装一个名为StreetMap的组件,代码如下 1 <template>2 <div>3 <div id=streetMap>4 <div class="tips" v-show="tipsShow" :style="tipsStyle"&g…

剥开比原看代码02:比原启动后去哪里连接别的节点

作者&#xff1a;freewind 比原项目仓库&#xff1a; Github地址&#xff1a;https://github.com/Bytom/bytom Gitee地址&#xff1a;https://gitee.com/BytomBlockchain/bytom 比原启动后去哪里连接别的节点 最开始我对于这个问题一直有个疑惑&#xff1a;区块链是一个分布式的…

RHEL6 Postfix+Dovecot邮件系统配置指南

在RHEL6上面折腾了下邮件的收发&#xff0c;rhel5默认采用sendmail做为MTA,rhel6则采用postfix&#xff0c;以前也从来没玩过这个&#xff0c;因此今天折腾的时候遇到了蛮多的阻力&#xff0c;现在基本实现了邮件的收发功能&#xff0c;更多的postfix配置后期还需要不断的学习和…

SVG图片绘制成不规则控件(中国省份为例)

activity_main: <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto&…

SVG标签属性viewbox的妙用

viewbox&#xff1a;可以根据父级大小缩放svg绘制出来的大小 <div class"h5_xp"><svg viewbox"0 0 640 640" width"100%" height"100%"><path d"M310 124 S315 140 300 134 L300 134 S294 120 310 124 L316 117 L…

python json删除字段_如何在Python中从JSON中删除括号?

我使用json.dumps将字典列表加载到JSON对象中。我的输出类似于&#xff1a;[ { "MetaData": {}, "SRData": { "ListOfLa311DeadAnimalRemoval": { "DeadAnimalRemoval": [ { "DACItemCount": "0", "DACType&…