摘要:對應(yīng)于繼續(xù),加入了超時對應(yīng)于終于到了包的調(diào)用,開始真正去連接這個種子節(jié)點(diǎn)了,到這里,我們可以認(rèn)為這個問題解決了。
作者:freewind
比原項(xiàng)目倉庫:
Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockc...
最開始我對于這個問題一直有個疑惑:區(qū)塊鏈?zhǔn)且粋€分布式的網(wǎng)絡(luò),那么一個節(jié)點(diǎn)啟動后,它怎么知道去哪里找別的節(jié)點(diǎn)從而加入網(wǎng)絡(luò)呢?
看到代碼之后,我才明白,原來在代碼中硬編碼了一些種子地址,這樣在啟動的時候,可以先通過種子地址加入網(wǎng)絡(luò)。雖然整個網(wǎng)絡(luò)是分布式的,但是最開始還是需要一定的中心化。
預(yù)編碼內(nèi)容對于配置文件config.toml,比原的代碼中硬編碼了配置文件內(nèi)容:
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,預(yù)設(shè)的種子是不同的。
當(dāng)然,如果我們自己知道某些節(jié)點(diǎn)的地址,也可以在初始化生成config.toml后,手動修改該文件添加進(jìn)去。
啟動syncManager那么,比原在代碼中是使用這些種子地址并連接它們的呢?關(guān)鍵在于,連接的代碼位于SyncManager中,所以我們要找到啟動syncManager的地方。
首先,當(dāng)我們使用bytomd node啟動后,下面的函數(shù)將被調(diào)用:
cmd/bytomd/commands/run_node.go#L41
func runNode(cmd *cobra.Command, args []string) error { // Create & start node n := node.NewNode(config) if _, err := n.Start(); err != nil { // ... } // ... }
這里調(diào)用了n.Start,其中的Start方法,來自于Node所嵌入的cmn.BaseService:
node/node.go#L39
type Node struct { cmn.BaseService // ... }
所以n.Start對應(yīng)的是下面這個方法:
vendor/github.com/tendermint/tmlibs/common/service.go#L97
func (bs *BaseService) Start() (bool, error) { // ... err := bs.impl.OnStart() // ... }
在這里,由于bs.impl對應(yīng)于Node,所以將繼續(xù)調(diào)用Node.OnStart():
node/node.go#L169
func (n *Node) OnStart() error { // ... n.syncManager.Start() // ... }
可以看到,我們終于走到了調(diào)用了syncManager.Start()的地方。
syncManager中的處理然后就是在syncManager內(nèi)部的一些處理了。
它主要是除了從config.toml中取得種子節(jié)點(diǎn)外,還需要把以前連接過并保存在本地的AddressBook.json中的節(jié)點(diǎn)也拿出來連接,這樣就算預(yù)設(shè)的種子節(jié)點(diǎn)失敗了,也還是有可能連接上網(wǎng)絡(luò)(部分解決了前面提到的中心化的擔(dān)憂)。
syncManager.Start()對應(yīng)于:
netsync/handle.go#L141
func (sm *SyncManager) Start() { go sm.netStart() // ... }
其中sm.netStart(),對應(yīng)于:
netsync/handle.go#L121
func (sm *SyncManager) netStart() error { // ... // If seeds exist, add them to the address book and dial out if sm.config.P2P.Seeds != "" { // dial out seeds := strings.Split(sm.config.P2P.Seeds, ",") if err := sm.DialSeeds(seeds); err != nil { return err } } // ... }
其中的sm.config.P2P.Seeds就對應(yīng)于config.toml中的seeds。關(guān)于這兩者是怎么對應(yīng)起來的,會在后面文章中詳解。
緊接著,再通過sm.DialSeeds(seeds)去連接這些seed,這個方法對應(yīng)的代碼位于:
netsync/handle.go#L229
func (sm *SyncManager) DialSeeds(seeds []string) error { return sm.sw.DialSeeds(sm.addrBook, seeds) }
其實(shí)是是調(diào)用了sm.sw.DialSeeds,而sm.sw是指Switch。這時可以看到,有一個叫addrBook的東西參與了進(jìn)來,它保存了該結(jié)點(diǎn)之前成功連接過的節(jié)點(diǎn)地址,我們這里暫不多做討論。
Switch.DialSeeds對應(yīng)于:
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]) } // ... }
這里引入了隨機(jī)數(shù),是為了將發(fā)起連接的順序打亂,這樣可以讓每個種子都獲得公平的連接機(jī)會。
sw.dialSeed(netAddrs[j])對應(yīng)于:
p2p/switch.go#L342
func (sw *Switch) dialSeed(addr *NetAddress) { peer, err := sw.DialPeerWithAddress(addr, false) // ... }
sw.DialPeerWithAddress(addr, false)又對應(yīng)于:
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參數(shù)如果是true的話,表明這個peer比較重要,在某些情況下如果斷開連接后,還會嘗試重連。如果persistent為false的,就沒有這個待遇。
newOutboundPeerWithConfig對應(yīng)于:
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) // ... }
繼續(xù)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對應(yīng)于:
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包的調(diào)用,開始真正去連接這個種子節(jié)點(diǎn)了,到這里,我們可以認(rèn)為這個問題解決了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/24160.html
摘要:所以這個文章系列叫作剝開比原看代碼。所以我的問題是比原初始化時,產(chǎn)生了什么樣的配置文件,放在了哪個目錄下下面我將結(jié)合源代碼,來回答這個問題。將用來確認(rèn)數(shù)據(jù)目錄是有效的,并且將根據(jù)傳入的不同,來生成不同的內(nèi)容寫入到配置文件中。 作者:freewind 比原項(xiàng)目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee...
摘要:作者比原項(xiàng)目倉庫地址地址在前一篇中,我們說到,當(dāng)比原向其它節(jié)點(diǎn)請求區(qū)塊數(shù)據(jù)時,會發(fā)送一個把需要的區(qū)塊告訴對方,并把該信息對應(yīng)的二進(jìn)制數(shù)據(jù)放入對應(yīng)的通道中,等待發(fā)送。這個就是真正與連接對象綁定的一個緩存區(qū),寫入到它里面的數(shù)據(jù),會被發(fā)送出去。 作者:freewind 比原項(xiàng)目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https:...
摘要:作者比原項(xiàng)目倉庫地址地址在前一篇中,我們已經(jīng)知道如何連上一個比原節(jié)點(diǎn)的端口,并與對方完成身份驗(yàn)證。代碼如下可以看到,首先是從眾多的中,找到最合適的那個。到這里,我們其實(shí)已經(jīng)知道比原是如何向其它節(jié)點(diǎn)請求區(qū)塊數(shù)據(jù),以及何時把信息發(fā)送出去。 作者:freewind 比原項(xiàng)目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://...
摘要:啟動直到進(jìn)入所以我們首先需要知道,比原在源代碼中是如何啟動,并且一步步走進(jìn)了的世界。后面省略了一些代碼,主要是用來獲取當(dāng)前監(jiān)聽的實(shí)際以及外網(wǎng),并記錄在日志中。 比原是如何監(jiān)聽p2p端口的 我們知道,在使用bytomd init --chain_id mainnet/testnet/solonet初始化比原的時候,它會根據(jù)給定的chain_id的不同,使用不同的端口(參看config/t...
摘要:如果傳的是,就會在內(nèi)部使用默認(rèn)的隨機(jī)數(shù)生成器生成隨機(jī)數(shù)并生成密鑰。使用的是,生成的是一個形如這樣的全球唯一的隨機(jī)數(shù)把密鑰以文件形式保存在硬盤上。 作者:freewind 比原項(xiàng)目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前一篇,我們探討了從瀏覽器的dashb...
閱讀 3428·2021-11-19 09:40
閱讀 1314·2021-10-11 11:07
閱讀 4844·2021-09-22 15:07
閱讀 2890·2021-09-02 15:15
閱讀 1964·2019-08-30 15:55
閱讀 539·2019-08-30 15:43
閱讀 883·2019-08-30 11:13
閱讀 1449·2019-08-29 15:36