您好,登錄后才能下訂單哦!
這篇文章主要講解了“Bytom的P2P網(wǎng)絡(luò)地址簿結(jié)構(gòu)體是怎樣的”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Bytom的P2P網(wǎng)絡(luò)地址簿結(jié)構(gòu)體是怎樣的”吧!
addrbook用于存儲(chǔ)P2P網(wǎng)絡(luò)中保留最近的對(duì)端節(jié)點(diǎn)地址 在MacOS下,默認(rèn)的地址簿路徑存儲(chǔ)在~/Library/Bytom/addrbook.json
** ~/Library/Bytom/addrbook.json **
{ "Key": "359be6d08bc0c6e21c84bbb2", "Addrs": [ { "Addr": { "IP": "122.224.11.144", "Port": 46657 }, "Src": { "IP": "198.74.61.131", "Port": 46657 }, "Attempts": 0, "LastAttempt": "2018-05-04T12:58:23.894057702+08:00", "LastSuccess": "0001-01-01T00:00:00Z", "BucketType": 1, "Buckets": [ 181, 10 ] } ] }
在addrbook中存儲(chǔ)的地址有兩種: ** p2p/addrbook.go **
const ( bucketTypeNew = 0x01 // 標(biāo)識(shí)新地址,不可靠地址(未成功連接過(guò))。只存儲(chǔ)在一個(gè)bucket中 bucketTypeOld = 0x02 // 標(biāo)識(shí)舊地址,可靠地址(已成功連接過(guò))??梢源鎯?chǔ)在多個(gè)bucket中,最多為maxNewBucketsPerAddress個(gè) )
<font color=red>注意: 一個(gè)地址的類型變更不在此文章中做介紹,后期的文章會(huì)討論該問(wèn)題</font>
地址簿
type AddrBook struct { cmn.BaseService mtx sync.Mutex filePath string // 地址簿路徑 routabilityStrict bool // 是否可路由,默認(rèn)為true rand *rand.Rand key string // 地址簿標(biāo)識(shí),用于計(jì)算addrNew和addrOld的索引 ourAddrs map[string]*NetAddress // 存儲(chǔ)本地網(wǎng)絡(luò)地址,用于添加p2p地址時(shí)做排除使用 addrLookup map[string]*knownAddress // 存儲(chǔ)新、舊地址集,用于查詢 addrNew []map[string]*knownAddress // 存儲(chǔ)新地址 addrOld []map[string]*knownAddress // 存儲(chǔ)舊地址 wg sync.WaitGroup nOld int // 舊地址數(shù)量 nNew int // 新地址數(shù)量 }
已知地址
type knownAddress struct { Addr *NetAddress // 已知peer的addr Src *NetAddress // 已知peer的addr的來(lái)源addr Attempts int32 // 連接peer的重試次數(shù) LastAttempt time.Time // 最近一次嘗試連接的時(shí)間 LastSuccess time.Time // 最近一次嘗試成功連接的時(shí)間 BucketType byte // 地址的類型(表示可靠地址或不可靠地址) Buckets []int // 當(dāng)前addr所屬的buckets }
routabilityStrict參數(shù)表示地址簿是否存儲(chǔ)的ip是否可路由??陕酚墒歉鶕?jù)RFC劃分,具體參考資料:RFC標(biāo)準(zhǔn)
// NewAddrBook creates a new address book. // Use Start to begin processing asynchronous address updates. func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook { am := &AddrBook{ rand: rand.New(rand.NewSource(time.Now().UnixNano())), ourAddrs: make(map[string]*NetAddress), addrLookup: make(map[string]*knownAddress), filePath: filePath, routabilityStrict: routabilityStrict, } am.init() am.BaseService = *cmn.NewBaseService(nil, "AddrBook", am) return am } // When modifying this, don't forget to update loadFromFile() func (a *AddrBook) init() { // 地址簿唯一標(biāo)識(shí) a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits // New addr buckets, 默認(rèn)為256個(gè)大小 a.addrNew = make([]map[string]*knownAddress, newBucketCount) for i := range a.addrNew { a.addrNew[i] = make(map[string]*knownAddress) } // Old addr buckets,默認(rèn)為64個(gè)大小 a.addrOld = make([]map[string]*knownAddress, oldBucketCount) for i := range a.addrOld { a.addrOld[i] = make(map[string]*knownAddress) } }
loadFromFile在bytomd啟動(dòng)時(shí),首先會(huì)加載本地的地址簿
// OnStart implements Service. func (a *AddrBook) OnStart() error { a.BaseService.OnStart() a.loadFromFile(a.filePath) a.wg.Add(1) go a.saveRoutine() return nil } // Returns false if file does not exist. // cmn.Panics if file is corrupt. func (a *AddrBook) loadFromFile(filePath string) bool { // If doesn't exist, do nothing. // 如果本地地址簿不存在則直接返回 _, err := os.Stat(filePath) if os.IsNotExist(err) { return false } // 加載地址簿json內(nèi)容 // Load addrBookJSON{} r, err := os.Open(filePath) if err != nil { cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err)) } defer r.Close() aJSON := &addrBookJSON{} dec := json.NewDecoder(r) err = dec.Decode(aJSON) if err != nil { cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err)) } // 填充addrNew、addrOld等 // Restore all the fields... // Restore the key a.key = aJSON.Key // Restore .addrNew & .addrOld for _, ka := range aJSON.Addrs { for _, bucketIndex := range ka.Buckets { bucket := a.getBucket(ka.BucketType, bucketIndex) bucket[ka.Addr.String()] = ka } a.addrLookup[ka.Addr.String()] = ka if ka.BucketType == bucketTypeNew { a.nNew++ } else { a.nOld++ } } return true }
bytomd會(huì)定時(shí)更新本地地址簿,默認(rèn)2分鐘一次
func (a *AddrBook) saveRoutine() { dumpAddressTicker := time.NewTicker(dumpAddressInterval) out: for { select { case <-dumpAddressTicker.C: a.saveToFile(a.filePath) case <-a.Quit: break out } } dumpAddressTicker.Stop() a.saveToFile(a.filePath) a.wg.Done() log.Info("Address handler done") } func (a *AddrBook) saveToFile(filePath string) { log.WithField("size", a.Size()).Info("Saving AddrBook to file") a.mtx.Lock() defer a.mtx.Unlock() // Compile Addrs addrs := []*knownAddress{} for _, ka := range a.addrLookup { addrs = append(addrs, ka) } aJSON := &addrBookJSON{ Key: a.key, Addrs: addrs, } jsonBytes, err := json.MarshalIndent(aJSON, "", "\t") if err != nil { log.WithField("err", err).Error("Failed to save AddrBook to file") return } err = cmn.WriteFileAtomic(filePath, jsonBytes, 0644) if err != nil { log.WithFields(log.Fields{ "file": filePath, "err": err, }).Error("Failed to save AddrBook to file") } }
當(dāng)peer之間交換addr時(shí),節(jié)點(diǎn)會(huì)收到對(duì)端節(jié)點(diǎn)已知的地址信息,這些信息會(huì)被當(dāng)前節(jié)點(diǎn)添加到地址簿中
func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() log.WithFields(log.Fields{ "addr": addr, "src": src, }).Debug("Add address to book") a.addAddress(addr, src) } func (a *AddrBook) addAddress(addr, src *NetAddress) { // 驗(yàn)證地址是否為可路由地址 if a.routabilityStrict && !addr.Routable() { log.Error(cmn.Fmt("Cannot add non-routable address %v", addr)) return } // 驗(yàn)證地址是否為本地節(jié)點(diǎn)地址 if _, ok := a.ourAddrs[addr.String()]; ok { // Ignore our own listener address. return } // 驗(yàn)證地址是否存在地址集中 // 如果存在:則判斷該地址是否為old可靠地址、是否超過(guò)了最大buckets中。否則根據(jù)該地址已經(jīng)被ka.Buckets引用的個(gè)數(shù)來(lái)隨機(jī)決定是否添加到地址集中 // 如果不存在:則添加到地址集中。并標(biāo)識(shí)為bucketTypeNew地址類型 ka := a.addrLookup[addr.String()] if ka != nil { // Already old. if ka.isOld() { return } // Already in max new buckets. if len(ka.Buckets) == maxNewBucketsPerAddress { return } // The more entries we have, the less likely we are to add more. factor := int32(2 * len(ka.Buckets)) if a.rand.Int31n(factor) != 0 { return } } else { ka = newKnownAddress(addr, src) } // 找到該地址在地址集的索引位置并添加 bucket := a.calcNewBucket(addr, src) a.addToNewBucket(ka, bucket) log.Info("Added new address ", "address:", addr, " total:", a.size()) }
地址簿中存儲(chǔ)眾多地址,在p2p網(wǎng)絡(luò)中需選擇最優(yōu)的地址去連接 PickAddress(newBias int)函數(shù)中newBias是由pex_reactor產(chǎn)生的地址評(píng)分。如何計(jì)算地址分?jǐn)?shù)在其他章節(jié)中再講 根據(jù)地址評(píng)分隨機(jī)選擇地址可增加區(qū)塊鏈安全性
// Pick an address to connect to with new/old bias. func (a *AddrBook) PickAddress(newBias int) *NetAddress { a.mtx.Lock() defer a.mtx.Unlock() if a.size() == 0 { return nil } // newBias地址分?jǐn)?shù)限制在0-100分?jǐn)?shù)之間 if newBias > 100 { newBias = 100 } if newBias < 0 { newBias = 0 } // Bias between new and old addresses. oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias)) newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias) // 根據(jù)地址分?jǐn)?shù)計(jì)算是否從addrOld或addrNew中隨機(jī)選擇一個(gè)地址 if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation { // pick random Old bucket. var bucket map[string]*knownAddress = nil num := 0 for len(bucket) == 0 && num < oldBucketCount { bucket = a.addrOld[a.rand.Intn(len(a.addrOld))] num++ } if num == oldBucketCount { return nil } // pick a random ka from bucket. randIndex := a.rand.Intn(len(bucket)) for _, ka := range bucket { if randIndex == 0 { return ka.Addr } randIndex-- } cmn.PanicSanity("Should not happen") } else { // pick random New bucket. var bucket map[string]*knownAddress = nil num := 0 for len(bucket) == 0 && num < newBucketCount { bucket = a.addrNew[a.rand.Intn(len(a.addrNew))] num++ } if num == newBucketCount { return nil } // pick a random ka from bucket. randIndex := a.rand.Intn(len(bucket)) for _, ka := range bucket { if randIndex == 0 { return ka.Addr } randIndex-- } cmn.PanicSanity("Should not happen") } return nil }
當(dāng)一個(gè)地址被標(biāo)記為Bad時(shí)則從地址集中移除。目前bytomd的代碼版本并未調(diào)用過(guò)
func (a *AddrBook) MarkBad(addr *NetAddress) { a.RemoveAddress(addr) } // RemoveAddress removes the address from the book. func (a *AddrBook) RemoveAddress(addr *NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.String()] if ka == nil { return } log.WithField("addr", addr).Info("Remove address from book") a.removeFromAllBuckets(ka) } func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) { for _, bucketIdx := range ka.Buckets { bucket := a.getBucket(ka.BucketType, bucketIdx) delete(bucket, ka.Addr.String()) } ka.Buckets = nil if ka.BucketType == bucketTypeNew { a.nNew-- } else { a.nOld-- } delete(a.addrLookup, ka.Addr.String()) }
感謝各位的閱讀,以上就是“Bytom的P2P網(wǎng)絡(luò)地址簿結(jié)構(gòu)體是怎樣的”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Bytom的P2P網(wǎng)絡(luò)地址簿結(jié)構(gòu)體是怎樣的這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。