【IPFS技术研究】(四)kbucket篇

在弄清IPFS的kademlia DHT之前,首先需要了解它的k桶原理,IPFS的kbucket路由表主要包含一个桶的大小(KValue = 20),一组k桶数组,其中每个k桶里面存放的是libp2p的节点id的双向链表。该路由表主要利用XOR算法计算与主机节点之间的逻辑距离,并分别将这些节点存放到不同的k桶中。IPFS默认k桶大小为20,libp2p节点peer.ID最大256位,理论上两个不同节点之间距离最大值是255,该路由表最多可以存放5120个libp2p节点。

基本数据结构

type RoutingTable struct {

    // ID of the local peer
    local ID

    // kBuckets define all the fingers to other nodes.
    Buckets    []*Bucket
    bucketsize int
}
type Bucket struct {
    lk   sync.RWMutex
    list *list.List
}

基本算法

1. XOR

libp2p节点id长度256位,由32个字节组成,XOR算法将节点id从低到高进行与操作。简单点讲两个位数相同则为0,不同则为1。

2. 节点距离 ZeroPrefixLen

两个节点之间的距离首先通过XOR算法计算出两个节点id之间的值,然后再从低到高位计算该值第一个非0的长度

任意一个节点计算自己节点id之间的距离必然是256,同理,如果有两个节点,一个节点第一个字节为0,一个节点第一个字节为1,那么对任意节点,这两个节点之中必然有一个节点与该任意节点之间的距离为0。

3. 比较Less

该比较算法直接把节点ID的32个字节从低到高比较字节之间的大小。

该算法主要用于在路由表中查找到节点数组后,再与自己的节点算出一个距离后,再进行排序。

主要接口

1. Update(p peer.ID)

1) 首先以目标节点id与自己节点id之间的距离,然后通过该距离找到对应的k桶,并将该节点id放入到该k桶最前面。

2) 如果该k桶容量超过了最大容量,那么

2.1)  如果不是最后一个桶,那么移除当前桶的最后一个节点id

2.2) 依次拆分最后一个桶,如果当前桶内节点id与主节点id距离为5的话,那么所有距离大于5的节点id都拆分到后面一个桶里

2. NearestPeers(id ID, count int)

 1) 根据id计算出与主节点id之间的距离,然后找到对应的k桶,然后从该k桶里面的所有节点id拷贝出来

 2) 如果该拷贝出来的数组容量低于count,则从该k桶前后两个k桶里面拷贝对应的节点id

 3) 对拷贝出来的节点进行排序,并返回最终的数组

小结

IPFS的模块化做得非常好,针对每一个模块,首先关注的是其测试用例,里面基本上能覆盖该模块所有主要路径。运行go test或者用dlv进行调试相关的内部逻辑。

并不是所有的的节点都能够加入到本地的路由表中,如下面实例

func TestTableFindMultiple(t *testing.T) {
    local := tu.RandPeerIDFatal(t)
    m := pstore.NewMetrics()
    rt := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m)

    peers := make([]peer.ID, 51200)
    for i := 0; i < 51200; i++ {
        peers[i] = tu.RandPeerIDFatal(t)
        rt.Update(peers[i])
    }
    rt.Print()

    t.Logf("totoal size is: %d", rt.Size())
}

最终返回的结果也就是241,很多前面的节点都会被淘汰掉

你可能感兴趣的:(【IPFS技术研究】(四)kbucket篇)