溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Bytom的P2P網(wǎng)絡(luò)地址簿結(jié)構(gòu)體是怎樣的

發(fā)布時(shí)間:2021-12-20 17:37:57 來(lái)源:億速云 閱讀:153 作者:iii 欄目:互聯(lián)網(wǎng)科技

這篇文章主要講解了“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介紹

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>

地址簿相關(guān)結(jié)構(gòu)體

地址簿

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)
	}
}

bytomd啟動(dòng)時(shí)加載本地地址簿

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
}

定時(shí)更新地址簿

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())
}

選擇最優(yōu)節(jié)點(diǎn)

地址簿中存儲(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
}

移除一個(gè)地址

當(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)注!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI