P2P学习——Kademlia的基础学习(易懂,但略长)

因为毕设的关系,得学习P2P的一些知识,然后就接触到了Kad。虽然经常吐槽百度搜不到我想要的东西,但还是习惯性的用百度搜了一些东西来看,但还是发现看不太懂,迷迷糊糊的。之后又维基百科了一下,然后在看了另外一篇csdn博客(链接在文后给出)之后,总算是有点明白了。

以下的内容是在个人理解的基础上进行铺叙的,所以描述的内容也主要以便于理解的方式进行展开,应该可以划分小的段落层次,但我并不认为那样到底在帮助阅读理解的方面有多大帮助。但作为基础,有些地方的具体细节可能还需要看相关的代码实现才能摸透,所以这里仅作为一个导读(?)。

以下内容中,很可能有用词,用法不当的地方,鉴于本人能力有限,并且这本来是一个同学之间的交流版本,所以对于专业术语等并不予以过多关注。

P2P网络结构,可以分三种:集中查询式,广播,DHT。其实,这一个网络中的资源始终是分散在各个节点上的,所以如何去区分网络结构,主要还是以如何查询为基础去区分的。

  KAD的一些细节:
    整个KAD网络中,维护着两个/两类字典:关键字字典和文件索引字典。之所以说是两类字典,实际上是因为是因为两个字典的所有内容/所有条目是分布的存储在参与KAD网络的节点中,对于整个KAD网络,字典只有两个,但对于具体的各个节点,字典却有许许多多个小块。
    就像我们使用一些BT站点,如六维空间(集中式)一样,如果我们要搜索某个资源,那么也是先通过关键字进行检索,然后选择满足我们需求的资源进行下载。KAD网络中的节点想要下载某个资源,也要通过通过网络中的关键字字典先获取可能的文件条目,然后利用文件条目的信息,在文件索引字典中,获取与资源对应的节点信息,然后开始下载。
    两个字典中的条目都是由<key, value>的键值对构成的,key 是一个160比特的SHA1散列,而value则是一个列表。在关键字字典中,key映射于关键字,value是一个包含特定关键字的文件列表,每一项包含文件名,文件大小和160位的SHA1文件校验值/文件ID,而在文件索引字典中,key映射于160位的SHA1文件校验值/文件ID,value是一个包含拥有该文件的节点的网络信息的列表,列表中的每个节点应该由<IP, port, Node-ID>来标识。其中Node-ID也是一个160比特的SHA1散列。
    如前所述,字典的所有条目分布的存储在KAD的网络节点中,也就是说,每个节点只需要知道字典中某一些条目内容就可以了,否则效率将不见得比集中式查询的网络高。从另一个角度上讲,因为没有集中式那样,需要一个中心服务器来维护当前网络中所有节点的信息,所以在KAD网络中,每个节点也需要负责维护一些网络中节点的信息。
    OK,到目前为止,KAD网络中的节点需要负责维护的信息有两种映射(关键字-->文件名,文件名-->节点信息)和其他节点的信息。前者与网络中的资源维护管理有关,后者与维护网络自身结构有关。
    以上的内容含有一个明显的问题——一个KAD网络中的节点,如何知道自己到底要负责维护哪些信息,即哪些关键字字典的条目,哪些文件索引字典的条目,以及维护哪些节点的状态。这也就是在,由对等节点构成的网络结构中,如何有效(低冗余,高查找效率)的保持整个系统的稳定性和性能的问题。
    为了解决这个问题,首先KAD做出了一种有效的关联:节点ID与字典的key对应。这或许就说明了为什么三者(关键字字典key,文件索引字典key,节点ID)都是160比特的SHA1散列。这样对于整个下载的两个阶段:检索和下载,发起调用的节点需要有的操作就变成了:访问保存某关键字的节点以获取文件ID,然后访问保存文件信息的节点以获取提供上载下载节点的ID,然后开始下载。例如,如果一个节点A(000111)想要查找关键字为"foo"的文件,并且该关键字在经过SHA1散列处理后的结果为010101,那么节点A就可以向网络中节点ID为010101的节点B发起询问,然后节点B向节点A回复关键字ID位010101所对应的文件信息(含文件ID,如010111),节点A在取得这个回复后,向节点ID为010111的节点C发起询问,节点C将存有文件ID为010111的那些节点的信息返回给节点A,之后节点A就可以向那些节点进行连接和下载了。
    可能有点混淆,这里再明确一下,整个KAD网络维护的是两类字典,关键字字典和文件索引字典。虽然两个字典的key都是160比特的SHA1散列,并且也都分别与节点ID进行了关联,但这两个字典却不能相互替代。毕竟他们的<key,value>对中的value不一样。这里不再赘述关键字技术对于搜索的意义。如何区分当前要检索哪个字典,浅显的猜测可能有:通过命令来区分,即远程调用的接受者通过命令的不同来区分检索哪个字典,或者通过当前的状态信息和value信息来区分,即远程调用的接受者不考虑当前应该检索哪个字典,只要将key满足的条目都发出去即可,而远程调用的发起者在接收到回复后,根据当前节点的状态和value的类型来区分和接收。出于对信息有效性的考虑,前者,即通过不同的远程调用命令来区分,似乎更能符合实际一些。
    然后为了能使整个系统的稳定性增强,以及为了提高查找效率,缓解热点等目的,KAD还做出这样一种规定,即KAD网络中的节点,会存储一些ID上邻近的节点的一些字典条目信息。
    如果当前系统中没有ID为010101的节点,例如具有该ID的节点已经下线了,或者目前没有节点使用/负责该ID,那么ID上邻近的节点(例如有010100,010110,010111)就需要负责存储节点ID为010101应该负责存储的信息。当然,可能当前没有资源被映射到这个ID,但这些邻近节点仍需要进行这样的负责。
    如果ID为010101的节点存在,那么在其他节点在查找资源的过程中,为了能够提高效率(提高命中范围,提前返回),邻近节点也有必要存储资源信息。同样,为了能缓解某一热点,某一节点被频繁查询的情况,邻近节点也有必要存储资源信息。
    这里需要说明一下的是:以上的邻近节点,是建立在节点ID上的一种逻辑上的邻近,并不是说两个节点物理距离,路由跳数上的邻近。这种逻辑上的邻近,是通过对160比特的SHA1散列进行按位异或的结果。逻辑上相邻的节点,其物理位置,IP位置上不相关,使得资源更能广泛的分布,不会因为断电断网等物理因素导致持有某一索引信息的节点集体消失。这种逻辑上的邻近,可以理解为字典条目上的邻近。因为我们已经按资源(关键字,文件)来划分了维护的任务。
    关键字字典的条目在文件基本无变更的情况下,基本不用担心维护的问题,毕竟由前所述,邻近节点会分担存储一些数据。而文件索引字典,因为存储的条目中包含的条目直接与具体的节点关联(<key,value>的value是以元组<IP, port, node-ID>为基本单元的列表),而网络中一个节点的变动比一个关键字,一个文件条目的变动更明显,所以,对于文件索引字典,需要有一些方法来维护,更新其存储的条目。(关键字"unix"对应的条目/那些文件,在很长一段时间内不会在网络中消失,如果负责这个关键字的节点离开了,那么在它的邻近节点也保存了关键字备份数据的情况下,这个节点的离开不会对整个KAD网络产生明显影响;但对于文件索引字典,负责文件"APUE"的节点则需要对value=<IP, port, node-ID>进行定期的维护,某些上载下载该文件的节点可能离开了或者新加入了)。
    接下来我们说明一下KAD网络节点是如何管理字典条目的。具体的说,就是节点是如何维护文件索引字典的,或者说是如何维护存储其他节点信息的路由表的。(路由表?没错,确实是路由表,因为文件ID已经于节点ID关联在一起了。<key, value> <==> <文件ID/节点ID, <节点的IP,端口,_ID> 。通过节点ID来查找节点的IP和端口,难道这不算路由?值得说一下的是这里的key与value是一对多的关系,所以 节点ID 和 与后面的 _ID 很可能没有相等的情况,不管某一ID有没有被节点使用,该ID对应的资源信息都会由邻近的ID来承担)。
    顺带一提的是,有关KAD的资料中,有叫做K-桶的概念,其实质应该是文件索引字典,只不过出于管理方便的目的,对字典进行了划分。一个节点中所有K-桶的数据之和,就是该节点所持有的文件索引字典的全部内容。
    网络中可能存在大量节点,并且每个节点自己的ID都是进入网络时随机生成的,因此为了便于管理和快速定位,对于每个节点,KAD将网络中的其他节点按照节点ID进行了划分。网上很多讲解KAD的资料中,都有说明KAD网络中将节点映射为一个二叉树的叶子节点的概念。这个概念在节点划分K-桶的时候是有用的,但不应该被这个树限制住。维基百科上的内容很有意思, 这里提取一下:Kademlia路由表由多个列表组成,每个列表对应节点ID的一位(节点ID共有160位,节点的路由表包含160个列表),列表包含多个条目。每个列表对应于与节点相距特定范围距离的一些节点,节点的第n个列表中所找到的节点的第n位与该节点的第n位肯定不同,而前n-1位相同,这就意味着很容易使用网络中远离该节点的一半节点来填充第一个列表(第一位不同的节点最多有一半),而用网络中四分之一的节点来填充第二个列表(比第一个列表中的那些节点离该节点更近一位),依次类推 。ID有160个二进制位,网络中的每个节点按照不同的异或距离把其他所有的节点分成了160类,ID的每一位对应于其中的一类。我觉的维基百科的内容足以说明一个节点是如何存储其他节点ID的。 高位上的不同,对异或的结果影响更大,因此由ID的位,能自然的联系到逻辑距离,进而一次来划分列表。 如果非要画出,或者理解那个二叉树的话,需要谨记的是,一个节点在二叉树中的位置是与其节点ID最短前缀对应的,例如如果一个节点ID是000XX...,而网络中的其他节点ID没有以000开头,那么这个以000开头的节点就可以挂在二叉树的000这个位置上,直到000不再是其节点ID最短前缀了。
    既然提到了K-桶,那么也需要说明一下。如前所述,基本上像上面维基百科的那一段所说的,K-桶的划分是以本节点存储的其他文件ID/节点ID相对于本节点的逻辑距离划分的,共有160个K-桶,这些K-桶分别存储与当前节点距离为[2^i, 2^(i+1) ) 的节点信息,其中 0≤i<160。作为分布式的网络结构,KAD网络中的节点没必要存储过多的关于本网络的信息。所以为了减少冗余,提高效率,每个K-桶中存储的数据量应不超过k个,k是一个系统级的必须为偶数的参数。典型的有 k=20,如果一个桶的序号i=3,那么就说明这个K-桶中存储的到当前节点距离为[8, 16)的节点最多有20个。k 是一个带有启发性质的估计值,挑选其取值的准则为:“在当前规模的Kad网络中任意选择至少k个节点,令它们在任意 时刻同时不在线的几率几乎为0”。
    那么要如何维护这些K-桶/列表呢?KAD不需要一个节点 定期的检查它 所保存(在文件索引字典/路由表中)的其他节点的状况(是否活动等)。那样会增加网络负担,并且也是没有效率的。可能有点令人不解,但请考虑这样一个静态统计分布规律: 那些长时间在线连接的节点未来长时间在线的可能性更大(不要收吸血骡的影响,这里说的是真正的P2P共享者)。对于一个加入KAD网络一段时间的节点,它所保存的那些节点如果都是按照这条规律筛选出来的,那么那些节点也一定是稳定的,将来也可以长时间在线的,那么本节点自然不需要对这些节点进行是否活动的检查,即使只保留这些稳定的节点,也足以维持自己所处的整个KAD网络子部分的稳定性了。KAD网络整体的稳定性也就建立在了这样大量子网络稳定性的基础上了。当有新的节点记录加入到本节点的维护数据/路由表中时,本节点会计算距离,检查对应的K-桶/列表,如果K-桶没有满,就将新的节点数据加到列表尾,如果K-桶满了,那么就要对列表头位置上的数据进行远程过程调用 PING 来检查其是否仍在活动,如果其仍在活动,那么如前所述,依概率,本节点维持现有数据能更优的维持稳定性,所以也就不需要新的节点信息了,节点信息被丢弃,如果检查的节点无法回应,即其已经不再活动了,那么就剔除它,然后将新的节点信息加入列表尾。
    在这里补充一下KAD的四个远程过程调用/四种消息:PING——用来测试节点是否仍然在线。STORE——指示其他节点存储一个键值对。FIND_NODE——消息请求的接收者将返回自己的K-桶中离请求键值最近的K个节点。FIND_VALUE——与FIND_NODE一样,不过当请求的接收者存有请求者所请求的键的时候,它将返回相应键的值。每一个RPC消息中都包含一个发起者加入的随机值,这一点确保响应消息在收到的时候能够与前面发送的请求消息匹配。
    在新的资源被发现,新的节点被发现,新的节点加入时,STORE会被用到。当根据资源ID(关键字ID,文件ID)来查找节点,而对应的节点不在本节点的记录中时,FIND_NODE会被用到。从FIND_VALUE与FIND_NODE的差别中可以认为: FIND_NODE是用来找到与目标ID最为接近的节点的,而FIND_VALUE是用来从当前获得的最接近节点上获取想要的数据的;二者的划分与在检索的字典是关键字字典还是文件索引字典无关,两种检索是相似的操作,因此都会用到这两个远程调用。
    既然上面的远程调用中说到了返回节点的概念,那么也就是和检索/路由某一节点有关了。在说明KAD中是如何查找节点的之前,我们先强调一些其他概念。到目前为止,我们应该知道,KAD网络中的每个节点都存储/维护着整个网络的数据的一个小部分,并且这部分中是含有冗余的。相对于整体的节点空间,一个节点在其文件索引字典/路由表中能够存储的节点数量也是有限的,对于典型的k=20的K-桶设置,一个节点最多能存储20*160=3200个节点,而整个节点空间能容纳的最大节点数量为2^160个。考虑资源和节点对于ID空间的分布的随机性,一个KAD网络中,可能对应某ID的文件存在,但是节点不存在,于是这个ID的近邻节点就要负责存储关于这个ID文件的信息;也有可能一个节点为自己生成了某个ID,但当前网络中没有分配到这个ID的资源存在,所以这个节点更多的任务,就是帮助邻近的节点负责资源。所以看似一个节点能存储不少其他节点的信息,但实际上完全可能当自己要检索某一资源时,在自己存储的数据中检索不到对应ID的节点,甚至连目标ID的邻近节点也没有存储到。因此,在KAD网络中,节点的定位/检索节点也是一个需要注意的问题。
    这里再次推荐维基百科的说明,百度上搜索的一些资料,在对这部分的内容讲解是配合一张二叉树的图的,我觉得那个图摆在那里反而具有某种混淆的作用,反而费解。这里摘要一下维基百科的内容: 节点查询可以异步进行,也可以同时进行,同时查询的数量由α表示,一般是3。一个节点在对其他节点进行节点查询时,先到K桶中找出离所查询的键值最近的K个节点,然后向这K个节点发起FIND_NODE消息请求,消息接收者会从他们的K桶中尽力返回离被查键更近的节点(最多K个) 。消息的请求者在收到响应后将使用它所收到的响应结果来更新它的结果列表,这个结果列表总是保持K个响应FIND_NODE消息请求的最优节点(即离被搜索键更近的K个节点)。然后消息发起者将向这K个最优节点发起查询,不断地迭代执行上述查询过程。因为每一个节点比其他节点对它逻辑相邻的节点有更好的感知能力,因此响应结果将是一次一次离被搜索键值越来越近的某节点。如果本次响应结果中的节点没有比前次响应结果中的节点离被搜索键值更近了,这个查询迭代也就终止了。当这个迭代终止的时候,响应结果集中的K个最优节点就是整个网络中离被搜索键值最近的K个节点(从以上过程看,这显然是局部的,而非整个网络)。 节点信息中可以增加一个往返时间,或者叫做RTT的参数,这个参数可以被用来定义一个针对每个被查询节点的超时设置,即当向某个节点发起的查询超时的时候,另一个查询才会发起,当然,针对某个节点的查询在同一时刻从来不超过α个。
    上面所说的结果列表不同于K-桶等通常情况下使用的列表。应该为了找到最邻近节点而临时使用的,毕竟在定位节点的过程中,没有必要去对已经稳定的记录进行改动。由前面介绍的如何维护K-桶的方法可知。当找到K个最邻近的节点时,如果本节点的对应的K-桶为空,那么这K个节点会被存储;如果对应的K-桶有一部分数据,那么就选取逻辑上最近邻的进行存储;如果K-桶已满了,那么询问当前桶中最早存入桶的数据,如果其还在活动,那么这K个节点在处理完本次的查询或存储等其他操作后就会被丢弃,如果最早进来的节点已经不在活动了,那么剔除K-桶中当前最早进入的节点,然后添加一个最近邻节点,然后重复这样的步骤,直到某次一个最早进入K-桶的节点还在活动,那么剩下的结果节点都会被丢弃。
    关于上述的在维护K-桶时,丢弃新节点的操作,可以选择设置一个cache来存储在K-桶已满的情况下发现的新节点,但这样做的意义不大。分析如下:(1)与其设置cache,不如扩大K-桶容量,设置的cache实质上近似于扩大K-桶容量,并且更复杂,(2)因为一个节点离开KAD网络时不会通知其他节点(没有意义,也不现实),所以如果想要cache起作用,那么就要定时检查K-桶中的节点是否还在活动,这样才能剔除离开的节点,将cache中的节点进行保存,如前所述这样做未被KAD的基础,并且(3)对于cache中的数据,无法确保其比已经在K-桶中的节点更可靠更能保证长时间活动。所以在维护K-桶的时候,将新的节点数据丢弃是最简单直接的方法,虽然还有其值得商榷的地方。(一种思路:以上说明的KAD维护K-桶的策略,实现了在K-桶不为空的情况下,K-桶节点全部离开的可能性极低的目标。但是这里仍然有改进的地方。简单丢弃的策略每次只检查最早进入K-桶的节点,这样可以保证其还在活动或至少检查其是否还在活动,但对于K-桶内出于中间进入位置的节点,不对其进行检查,也无法判断其是否仍存在。如果最早进入K-桶的节点健壮性过强,那么当K-桶满了的情况下,如果定位的节点不在K-桶内,那么当尝试以当前最近邻的节点为基础进行递归定位的时候,对于进入K-桶的中间位置的节点已经离开的状态,则要承担效率上的一些代价。如果在每次维护K-桶的时候,能够对中间的某些节点也进行检查,那么K-桶内有效节点的数量将得到更多的保证。在尽量不影响单次K-桶内节点检查的效率情况下,对于K=8(BittTorrent的K值),如果每次能按照(0,1),(0,2),(0,3),(0,4), (0,5), (0,6), (0,7)的次序进行有效的检查,即每次出了检查最早进入K-桶的节点外,再额外检查一个节点,那么经过几次检查,就可以对桶内的数据做了一次相对比较完整的检查)。
    剩下的基础内容还有一个节点加入KAD网络所需要的引导过程,但就不在这里多说了。

这里给出在我觉得有主要帮助的链接:
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值