在Torrent Kademlia路由表中实现查找节点

6
我已经审查了一些关于此主题的文件,但有些不太清楚。例如BitTorrent文档(http://www.bittorrent.org/beps/bep_0005.html)中提到:
路由表被细分为“桶”,每个桶覆盖空间的一部分。一个空表只有一个ID范围为 min=0,max=2^160 的桶。当插入一个ID为“N”的节点到表中时,它被放置在具有min ≤ N < max的桶内。每个桶最多只能容纳K个节点,目前为8个,超过则为“满”。当桶中存在已知良好节点时,不允许添加更多节点,除非我们自己的节点ID落在桶的范围之内。在这种情况下,该桶将被替换为两个新的桶,每个桶的大小是旧桶的一半,并且来自旧桶的节点将分配给这两个新桶。对于只有一个桶的新表格,满桶总是分裂成涵盖范围0..2^159和2^159..2^160的两个新桶。
这与其他关于Kademlia路由表的文件有些不同,其中桶按照节点ID的位前缀排序,但还有另一件令人困惑的事情。当我们回复“查找节点”请求时,我们必须使用XOR操作找到8个最接近请求节点的节点。我看到一些实现只是遍历路由表中的每个项执行XOR操作,从而查找8个最接近的项。这对CPU来说似乎太浪费了。
所有东西都已经在桶里了。即使我们使用BitTorrent文档建议的系统,我们也可以更快地找到可能包含所需节点ID的桶,只需枚举桶并检查其上的最小和最大数字即可。然后,该桶可能应该包含最接近的节点,但它们是值最接近的节点而不是XOR最接近的节点(据我理解),尽管两者有些相似但还是有些不同。
我运行了一个简单的测试,使用从0到99的数字,想要找到8个XOR最接近的数字,它们靠近所寻找的数字,但不是紧挨着的。现在,考虑到我们的桶,我猜所有桶中的节点ID都是最接近的,只有少数例外。因此,例如,如果我们取出这个桶中的左侧和右侧各一个,并搜索XOR最接近的节点ID,我们将找到我们要查找的内容,没有必要遍历路由表中的所有节点。
我是正确的还是遗漏了什么?

经过一些测试,我发现我的先前答案实际上是不正确的,已经更新以反映正确和经过测试的算法。 - the8472
1个回答

5
这份文件在kademlia路由表方面与其他文档有些不同,其中桶按节点id的位前缀排列,但还有另一件令人困惑的事情。比特流传规范描述了一个路由表实现,只近似于kademlia论文中描述的实现。它更容易实现,但有一些缺点。因此,例如,如果我们从左边和右边取一个桶并搜索XOR最接近的节点ID,我们将找到我们要寻找的内容,而且没有必要遍历路由表中的所有节点。在完整的树状路由表实现和简化的BEP5变体中,每个桶都可以看作具有CIDR样式前缀(请参见this SO answer),包括桶覆盖的前缀位和掩码位计数。
在BEP5变种中,每个bucket的前缀仅从数组索引和您的节点ID派生。在树状表中,由于bucket的拆分/合并操作,必须跟踪它们的前缀。
使用这些前缀,您可以找到覆盖目标密钥的bucket。
问题是bucket不必填满,如果您想要在响应中发送20个节点,则单个bucket将不足以满足需求。
因此,您必须按升序(XOR)顺序遍历路由表(基于自己的节点ID或自然距离排序),相对于目标键访问多个buckets。
由于XOR距离度量在每个位进位处折叠(XOR ==无进位加法),因此它不能很好地映射到任何路由表布局。换句话说,访问最近的buckets行不通。 这是我实现的 从树状路由表中找到特定目标键的N个最接近节点的方法。
我认为许多人只是遍历整个路由表,因为对于常规节点来说,它最多只包含几十个桶,而DHT节点不会看到太多的流量,因此它每秒只需要执行这个操作几次。如果您在稠密、缓存友好的数据结构中实现这个操作,那么绝大部分可能实际上是内存流量,而不是CPU指令执行几个XOR和比较操作。
即全表扫描只是易于实现的。

假设我们有一个路由表,每个桶都有以下位前缀。字母作为方便的名称。

A 000... 
B 00100... 
C 0010100... 
D 0010101000... 
E 0010101001...
F 001010101... 
G 00101011... 
H 001011... 
I 0011... 
J 01... 
K 1... 

现在假设我们正在寻找这个目标键:

T = 0010011010111111001111100101011000001010010100001100100010011100000000100100000111001101100110110110101100010100111010111001101111110101001001001000100000001001

此外,存储桶并不完全填满,或者我们需要更多的条目才能适合单个存储桶,因此我们必须访问多个存储桶以获取所需数量。现在,第一个要访问的存储桶相当明显,它是 B,因为它的前缀覆盖了目标键。由于 B 的前缀长度为 5 位,因此该存储桶中的任何条目与 T 的异或距离都为 00000???????...,共享 5 位前缀位。B 是距离 T 最近的存储桶,这意味着不能有比相对距离 00000... 更接近的路由表条目。反之,这意味着 B 以外的任何条目的最小距离都可以为 00001...。这意味着下一个最接近的存储桶必须覆盖 T xor 00001... -> 00101110101111110[...]。覆盖它的存储桶是 H。H 不邻接 B。最终 - 假设我们必须访问 6 个存储桶 - 访问顺序如下:
00100...      -> B
001011...     -> H
001010101...  -> F
0010101000... -> D
0010101001... -> E
00101011...   -> G

这看起来相当混乱。但是,如果我们绘制每个访问的桶的前缀到目标键的距离,情况就会变得更加明显:

00000...
000010...
000011000...
0000110010...
0000110011...
00001101...

所以算法如下:
  1. 找到覆盖目标键的初始桶
  2. 将桶的前缀与目标键进行异或运算(零掩码尾部位)
  3. 通过该前缀的最低有效位增加距离
  4. 将增加后的距离与目标键进行异或运算
  5. 找到覆盖XORed键的下一个桶
  6. 转到步骤2
简而言之:仅查看左右一个桶是不够的。正确的算法相当复杂,对整个表进行线性扫描更容易实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接