Kademlia 协议原理简介



一、前言
Kademlia协议(以下简称Kad)是美国纽约大学的PetarP. Maymounkov和David Mazieres.
在2002年发布的一项研究结果《Kademlia: A peerto -peer information system based on
the XOR metric》。
简单的说,Kad 是一种分布式哈希表(DHT)技术,不过和其他DHT 实现技术比较,如
Chord、CAN、Pastry 等,Kad 通过独特的以异或算法(XOR)为距离度量基础,建立了一种
全新的DHT拓扑结构,相比于其他算法,大大提高了路由查询速度。
在 2005 年5 月著名的BiTtorrent 在4.1.0 版实现基于Kademlia 协议的DHT 技术后,
很快国内的BitComet 和BitSpirit 也实现了和BitTorrent 兼容的DHT 技术,实现
trackerless下载方式。
另外,emule 中也很早就实现了基于Kademlia类似的技术(BT中叫DHT,emule中也叫
Kad,注意和本文简称的Kad 区别),和BT 软件使用的Kad 技术的区别在于key、value 和
node ID 的计算方法不同。
二、节点状态
在 Kad 网络中,所有节点都被当作一颗二叉树的叶子,并且每一个节点的位置都由其ID
值的最短前缀唯一的确定。
对于任意一个节点,都可以把这颗二叉树分解为一系列连续的,不包含自己的子树。最
高层的子树,由整颗树不包含自己的树的另一半组成;下一层子树由剩下部分不包含自己的
一半组成;依此类推,直到分割完整颗树。图1就展示了节点0011 如何进行子树的划分:
图 1:节点0011的子树划分
虚线包含的部分就是各子树,由上到下各层的前缀分别为0,01,000,0010。
Kad 协议确保每个节点知道其各子树的至少一个节点,只要这些子树非空。在这个前提
下,每个节点都可以通过ID值来找到任何一个节点。这个路由的过程是通过所谓的XOR(异
或)距离得到的。
图 2 就演示了节点0011 如何通过连续查询来找到节点1110 的。节点0011 通过在逐步
底层的子树间不断学习并查询最佳节点,获得了越来越接近的节点,最终收敛到目标节点上。
图2:通过ID值定位目标节点
需要说明的是,只有第一步查询的节点101,是节点0011已经知道的,后面各步查询的
节点,都是由上一步查询返回的更接近目标的节点,这是一个递归操作的过程。
三、节点间距离
Kad 网络中每个节点都有一个160bit 的ID 值作为标志符,Key也是一个160bit 的标志
符,每一个加入Kad网络的计算机都会在160bit 的key空间被分配一个节点ID(node ID)
值(可以认为ID 是随机产生的),<key,value>对的数据就存放在ID 值“最”接近key 值的
节点上。
判断两个节点x,y 的距离远近是基于数学上的异或的二进制运算,d(x,y) = x⊕y,既
对应位相同时结果为0,不同时结果为1。例如:
010101
XOR 110001
-------------
100100
则这两个节点的距离为32+4=36。
显然,高位上数值的差异对结果的影响更大。
对于异或操作,有如下一些数学性质:
l d(x, x) = 0
l d(x, y) > 0, if x ≠ y
l ∨x, y : d(x, y) = d(y, x)
l d(x, y) + d(y, z) ≧ d(x, z)
l d(x, y) ⊕ d(y, z) = d(x, z)
l ∨a≧ 0, b≧ 0, a + b≧ a ⊕ b
正如Chord 的顺时针旋转的度量一样,异或操作也是单向性的。对于任意给定的节点x
和距离⊿≧0,总会存在一个精确的节点y,使得d(x,y)= ⊿。另外,单向性也确保了对于
同一个key值的所有查询都会逐步收敛到同一个路径上,而不管查询的起始节点位置如何。
这样,只要沿着查询路径上的节点都缓存这个<key,value>对,就可以减轻存放热门key 值
节点的压力,同时也能够加快查询响应速度。
四、K 桶
Kad 的路由表是通过一些称之为K 桶的表格构造起来的。这有点类似Tapestry技术,其
路由表也是通过类似的方法构造的。
对每一个0≦i≦160,每个节点都保存有一些和自己距离范围在区间[2i , 2i+1)内的一些
节点信息,这些信息由一些(IP address,UDP port,Node ID)数据列表构成(Kad 网络是靠
UDP 协议交换信息的)。每一个这样的列表都称之为一个K 桶,并且每个K 桶内部信息存放
位置是根据上次看到的时间顺序排列,最近(least-recently)看到的放在头部,最后
(most-recently)看到的放在尾部。每个桶都有不超过k 个的数据项。
一个节点的全部K 桶列表如表1 所示:
I 距离邻居
0 [20 ,21) 0-1
0-k
(IP address,UDP port,Node ID)
......
(IP address,UDP port,Node ID)
1 [21,22 ) 1-1
1-k
(IP address,UDP port,Node ID)
......
(IP address,UDP port,Node ID)
2 [22 , 23 ) 2-1
2-k
(IP address,UDP port,Node ID)
......
(IP address,UDP port,Node ID)
……
i [2i , 2i+1)
i-1
i-k
(IP address,UDP port,Node ID)
......
(IP address,UDP port,Node ID)
……
160 [2160 ,2161) 160-1
160-k
(IP address,UDP port,Node ID)
......
(IP address,UDP port,Node ID)
表 1:K 桶结构
不过通常来说当i 值很小时,K 桶通常是空的(也就是说没有足够多的节点,比如当i
=0 时,就最多可能只有1 项);而当i 值很大时,其对应K 桶的项数又很可能会超过k 个
(当然,覆盖距离范围越广,存在较多节点的可能性也就越大),这里k 是为平衡系统性能
和网络负载而设置的一个常数,但必须是偶数,比如k=20。在BitTorrent 的实现中,取
值为k=8。
由于每个K桶覆盖距离的范围呈指数关系增长,这就形成了离自己近的节点的信息多,
离自己远的节点的信息少,从而可以保证路由查询过程是收敛。因为是用指数方式划分区
间,经过证明,对于一个有N 个节点的Kad网络,最多只需要经过logN步查询,就可以准
确定位到目标节点。这个特性和Chord 网络上节点的finger table 划分距离空间的原理类
似。
当节点 x 收到一个PRC 消息时,发送者y 的IP 地址就被用来更新对应的K 桶,具体步
骤如下:
1.计算自己和发送者的距离:d(x,y) = x⊕y,注意:x 和y 是ID 值,不是IP 地址
2.通过距离d 选择对应的K 桶进行更新操作。
3.如果y 的IP 地址已经存在于这个K桶中,则把对应项移到该该K 桶的尾部
4.如果y 的IP 地址没有记录在该K 桶中
⑴如果该K 桶的记录项小于k 个,则直接把y 的(IP address,UDP port,Node ID)
信息插入队列尾部
⑵如果该K桶的记录项大于k个,则选择头部的记录项(假如是节点z)进行RPC_PING
操作
①如果z 没有响应,则从K 桶中移除z的信息,并把y 的信息插入队列尾部
②如果z 有响应,则把z 的信息移到队列尾部,同时忽略y 的信息。
K 桶的更新机制非常高效的实现了一种把最近看到的节点更新的策略,除非在线节点一
直未从K桶中移出过。也就是说在线时间长的节点具有较高的可能性继续保留在K桶列表中。
采用这种机制是基于对Gnutella 网络上大量用户行为习惯的研究结果,既节点的失效
概率和在线时长成反比关系,如图3(横坐标为分钟,纵坐标为概率):
图3:Gnutella 网络中在线时长和继续在线的概率关系
可以明显看出,用户在线时间越长,他在下一时段继续在线的可能性就越高。
所以,通过把在线时间长的节点留在K 桶里,Kad 就明显增加K 桶中的节点在下一时间
段仍然在线的概率,这对应Kad网络的稳定性和减少网络维护成本(不需要频繁构建节点的
路由表)带来很大好处。
这种机制的另一个好处是能在一定程度上防御DOS 攻击,因为只有当老节点失效后,Kad
才会更新K桶的信息,这就避免了通过新节点的加入来泛洪路由信息。
为了防止K桶老化,所有在一定时间之内无更新操作的K 桶,都会分别从自己的K 桶中
随机选择一些节点执行RPC_PING 操作。
上述这些K 桶机制使Kad 缓和了流量瓶颈(所有节点不会同时进行大量的更新操作),
同时也能对节点的失效进行迅速响应。
五、Kademlia 协议操作类型
Kademlia 协议包括四种远程RPC 操作:PING、STORE、FIND_NODE、FIND_VALUE。
1、PING操作的作用是探测一个节点,用以判断其是否仍然在线。
2、STORE操作的作用是通知一个节点存储一个<key,value>对,以便以后查询需要。
3、FIND_NODE操作使用一个160bit 的ID 作为参数。本操作的接受者返回它所知道的更
接近目标ID的K 个节点的(IP address,UDP port,Node ID)信息。
这些节点的信息可以是从一个单独的K桶获得,也可以从多个K桶获得(如果最接近目
标ID 的K 桶未满)。不管是哪种情况,接受者都将返回K 个节点的信息给操作发起者。但如
果接受者所有K 桶的节点信息加起来也没有K 个,则它会返回全部节点的信息给发起者。
4、FIND_VALUE 操作和FIND_NODE 操作类似,不同的是它只需要返回一个节点的(IP
address,UDP port,Node ID)信息。如果本操作的接受者收到同一个key的STORE 操作,则
会直接返回存储的value 值。
注:在Kad 网络中,系统存储的数据以<key,value>对形式存放。根据笔者的分析,在
BitSpirit的DHT实现中,其key值为torrent文件的info_hash串,其value值则和torrent
文件有密切关系。
为了防止伪造地址,在所有RPC 操作中,接受者都需要响应一个随机的160bit的ID值。
另外,为了确信发送者的网络地址,PING 操作还可以附带在接受者的RPC回复信息中。
六、路由查询机制
Kad 技术的最大特点之一就是能够提供快速的节点查找机制,并且还可以通过参数进行
查找速度的调节。
假如节点x要查找ID值为t 的节点,Kad按照如下递归操作步骤进行路由查找:
1、计算到t 的距离:d(x,y) = x⊕y
2、从 x 的第[㏒ d]个K 桶中取出α 个节点的信息(“[”“]”是取整符号),同时进行
FIND_NODE 操作。如果这个K 桶中的信息少于α 个,则从附近多个桶中选择距离最
接近d 的总共α个节点。
3、对接受到查询操作的每个节点,如果发现自己就是t,则回答自己是最接近t 的;
否则测量自己和t 的距离,并从自己对应的K 桶中选择α 个节点的信息给x。
4、X 对新接受到的每个节点都再次执行FIND_NODE 操作,此过程不断重复执行,直到
每一个分支都有节点响应自己是最接近t 的。
5、通过上述查找操作,x 得到了k 个最接近t 的节点信息。
注意:这里用“最接近”这个说法,是因为ID 值为t 的节点不一定存在网络中,也就
是说t 没有分配给任何一台电脑。
这里α 也是为系统优化而设立的一个参数,就像K一样。在BitTorrent 实现中,取值
为α=3。
当 α=1 时,查询过程就类似于Chord的逐跳查询过程,如图4。
图4:α=1时的查询过程
整个路由查询过程是递归操作的,其过程可用数学公式表示为:
0 n = x(即查询操作的发起者)
0
1
1
1
2
_ ( )
_ ( )
......
_ ( ) l
n
n
l n
N find node t
N find node t
N find node t -
=
=
=
这个递归过程一直持续到l N =t,或者l N 的路由表中没有任何关于t 的信息,即查询
失败
由于每次查询都能从更接近t的K 桶中获取信息,这样的机制保证了每一次递归操作都
能够至少获得距离减半(或距离减少1bit)的效果,从而保证整个查询过程的收敛速度为
O(logN),这里N 为网络全部节点的数量。
当节点 x 要查询<key,value>对时,和查找节点的操作类似,x选择k个ID 值最接近key
值的节点,执行FIND_VALUE 操作,并对每一个返回的新节点重复执行FIND_VALUE 操作,直
到某个节点返回value 值。
一旦FIND_VALUE 操作成功执行,则<key,value>对数据会缓存在没有返回value 值的最
接近的节点上。这样下一次查询相同的key 时就会更加快速的得到结果。通过这样的方式,
热门<key,value>对数据的缓存范围就逐步扩大,使系统具有极佳的响应速度,如图5所示。
图 5:缓存原则
七、数据存放
存放<key,value>对数据的过程为:
1、发起者首先定位k 个ID 值最接近key 的节点;
2、发起者对这k 个节点发起STORE 操作
3、执行STORE操作的k个节点每小时重发布自己所有的<key,value>对数据。
4、为了限制失效信息,所有<key,value>对数据在初始发布24 小时后过期。
另外,为了保证数据发布、搜寻的一致性,规定在任何时候,当节点w 发现新节点u比
w 上的某些<key,value>对数据更接近,则w把这些<key,value>对数据复制到u 上,但是并
不会从w 上删除。
八、节点加入和离开
如果节点u 要想加入Kad 网络,它必须要和一个已经在Kad 网络的节点,比如w,取得
联系。
u 首先把w 插入自己适当的K桶中,然后对自己的节点ID 执行一次FIND_NODE操作,然
后根据接收到的信息更新自己的K 桶内容。通过对自己邻近节点由近及远的逐步查询,u完
成了仍然是空的K 桶信息的构建,同时也把自己的信息发布到其他节点的K 桶中。
在 Kad 网络中,每个节点的路由表都表示为一颗二叉树,叶子节点为K 桶,K 桶存放的
是有相同ID 前缀的节点信息,而这个前缀就是该K 桶在二叉树中的位置。这样,每个K 桶
都覆盖了ID 空间的一部分,全部K 桶的信息加起来就覆盖了整个160bit的ID 空间,而且
没有重叠。
以节点u 为例,其路由表的生成过程为:
1.最初,u 的路由表为一个单个的K 桶,覆盖了整个160bitID 空间,如图6 最上面的
路由表;
2.当学习到新的节点信息后,则u 会尝试把新节点的信息,根据其前缀值插入到对应
的K 桶中:
① 如果该K 桶没有满,则新节点直接插入到这个K桶中;
② 如果该K 桶已经满了,
⑴ 如果该K 桶覆盖范围包含了节点u的ID,则把该K 桶分裂为两个大小相同
的新K 桶,并对原K桶内的节点信息按照新的K桶前缀值进行重新分配
⑵ 如果该K 桶覆盖范围没有包节点u的ID,则直接丢弃该新节点信息
3.上述过程不断重复,最终会形成表1 结构的路由表。达到距离近的节点的信息多,
距离远的节点的信息少的结果,保证了路由查询过程能快速收敛。
图 6:节点000的路由表生成演化
在图7 中,演示了当覆盖范围包含自己ID 值的K 桶是如何逐步分裂的。
图7:节点0100的K桶分裂过程
当 K 桶010 满了之后,由于其覆盖范围包含了节点0100的ID,故该K 桶分裂为两个新
的K 桶:0101和0100,原K 桶010 的信息会根据其其前缀值重新分布到这两个新的K 桶中。
注意,这里并没有使用160bit 的ID 值表示法,只是为了方便原理的演示,实际Kad 网络中
的ID 值都是160bit的。
节点离开Kad网络不需要发布任何信息,Kademlia 协议的目标之一就是能够弹性工作在
任意节点随时失效的情况下。为此,Kad 要求每个节点必须周期性的发布全部自己存放的
<key,value>对数据,并把这些数据缓存在自己的k 个最近邻居处,这样存放在失效节点的
数据会很快被更新到其他新节点上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值