kademlia(卡德米利亚)算法原理介绍

前言

感觉碰到这个算法的过程有点莫名其妙?本来是老老实实学习计算机网络协议的,碰到遇到了应用层的p2p协议,然后就了解了分布式网络,然后就了解了DHT,最后就到了这个kad算法(其实人家是一个协议,实现了DHT好吧)。开整!

分布式海量存储结构

我写这个标题就有点唬人,不过确实这种结构可以适合海量数据的存储。这是一种去中心化的存储,在整个网络中node与node之间的传输查询或者其他操作都是没有中心服务器的参与的。放张图可能你就能理解了。
在这里插入图片描述
老的p2p协议确实在数据传输上是peer to peer的,不需要中心服务器tracker的参与。可是某一peer请求数据的时候,需要先访问tracker服务器,然后服务器告诉该peer,哪个peer上有你要的资源,然后把对应的ip地址给你。这一操作就差点意思了兄弟,要是tracker服务器坏掉了,那不就直接GG?
于是呢,一种分布式存储结构就出现了,叫distribute_hash_table,即分布式哈希表,它是用来干啥的呢?反正从我的角度看,DHT直接干掉了tracker服务器,让网络中的每一个节点都有着相等的地位,且互相交换数据做到了真正的peer to peer。现在是不是非常好奇,究竟是如何实现的呢?

逻辑上的结构

首先呢,每一个加入网络的家伙,都会根据哈希算法,生成一个独特的节点id,长度呢就看你喜欢了,比如说长度是200bit。(长度大家都是固定一样的)然后呢,逻辑上将所有的节点放在一颗巨大无比的二叉树中。肯定没有真的放进去对吧,这就是虚拟出来的,朋友,懂么?就像你的男朋友或者女朋友一样,都是你虚拟出来的,是不存在的。
OK那怎么虚拟的将节点放到二叉树中呢?很简单,将节点的id从最高位开始,同时将虚拟二叉树从根节点开始,遇到id某bit是0则虚拟二叉树向左走一步,反之则向右走一步。其实最后就200层的虚拟二叉树对吧,所有的节点都存放在最下面一层。
在这里插入图片描述
OK那个小黑点就是咱自己节点。好了,现在要从自己的角度出发,将这颗虚拟的3二叉树进行一个模块划分(先别管为啥要划分好吧),本质上讲,就是咱们自己节点的父亲节点不断的上升,然后呢,总有一部分是刚刚升上来的,另一部分则不是,于是你看,这个二叉树就分成了除自己以外的三个部分。(咱们这棵树的节点层数可是200层,就能分成200个部分)
我不得不说,这种想法是真的棒。

逻辑上的节点与节点间距

咱们到现在为止都还是在说逻辑上,对吧。逻辑上咱们将所有的点都放在一颗虚拟二叉树的叶子节点上,现在我想求一下两个叶子节点的虚拟间距,咋搞?简单啊,把hash得到的节点id异或一下啊!
节点A的ID(010)
节点B的ID(110)
A ⊕ B = 100(二进制) = 4(十进制)
是不是被震惊到了,没想到吧?所以我才觉得这个虚拟二叉树的理念是真的棒。
那我为什么要从咱自己这个节点的角度对这颗虚拟二叉树进行划分呢?其实是这个原因:对于每个子树,如果我们分别知道里面的一个节点,就可以利用这个节点递归路由到子树的任意一个节点。听起来好像很有道理。但是在实际应用中,由于节点是动态增加减少的,如果知道的节点恰好宕机或者下线了就会出现问题,为了保证系统的鲁棒性Kad算法又引入了K桶(K-bucket)的机制。

K bucket

这个桶长啥样子呢?其实就是个链表,表示在一定的偏移量范围内的K个节点的ip地址信息。这么多个桶串联起来成了一张表,这个表其实就是路由表!
在这里插入图片描述

如何更新K bucket中的节点信息

为什么要更新呢?因为每时每刻都会有一些节点永久消失掉。这当然很好理解,比如某个人很长时间都没上线了,它把某个BT软件卸载了,都是很可能发生的对吧,所以咱们要更新这个K bucket。
以下三种方式用来更新k bucket:
1.周期性的发起PING请求,判断K桶中某个节点是否在线,然后清理K桶中哪些下线的节点。
2.主动收集节点,主动发起FIND_NODE查询节点的请求,从而更新K桶的节点信息
3.被动收集节点,当收到其他节点发送过来的请求(如:FIND_NODE、FIND_VALUE),会把对方的节点ID加入到某个K桶中
当确定某个节点ID需要被加入到K bucket中时,怎么搞呢?
1.计算自己和目标节点ID的距离d
2.通过距离d找到对应的K桶,如果ID已经在K桶中了则把对应项移到K桶的末尾
3.如果不在K桶中则有两种情况
3.1.如果该K桶存储的节点小于K个,则直接把目标节点插入到K-桶尾部;
3.2.如果该K桶存储节点大于等于K个,则选择K-桶中的头部节点进行PING操作,检测节点是否存活。如果头部节点没有响应,则移除该头部节点,并将目标节点插入到队列尾部;如果头部节点有响应,则把头部节点移到队列尾部,同时忽略目标节点。

一个崭新的节点希望加入Kad网络

一个新节点需要加入Kad网络有如下的步骤:
1.新节点A生成一个随机的节点ID,直到离开网络一直使用。
2.新节点A需要一个种子节点B作为引导,并把该种子节点加入到K桶中。
3.向节点B发送FIND_NODE请求。
4.节点B在收到节点A的FIND_NODE请求后,会根据FIND_NODE请求的约定,找到K个距离A最近的节点,并返回给A节点,A收到这些节点以后,就把它们加入到自己的K桶中
5.然后节点A会继续向这些刚拿到节点发起FIND_NODE请求,如此往复,直到A建立了足够详细的路由表。

如何find_node?

1.确定目标ID对应路由表中的K桶位置,然后从自己的K-桶中筛选出K个距离目标ID最近的节点,并同时向这些节点发起FIND_NODE的查询请求。
2.被查询节点收到FIND_NODE请求后,从对应的K桶中找出自己所知道的最近的K个节点,并返回给发起者。
3.发起者在收到这些节点后,更新自己的结果列表,并再次从其中K个距离目标节点ID最近的节点,挑选未发送请求的节点重复第一步
4.不断重复上面的步骤直到找到目标节点为止

如何find_value

其实吧,不仅仅是节点是hash后的一串id,就连文件都是哈希后的id(对文件名称字符串进行哈希,当然可以呀,而且那些MD5或者SHA不都可以么?)有这么个规定,就是说,节点id和文件id相同的话,那么这个节点以及节点的邻近K个节点都有责任知道,这个文件所在的节点id。其实只要这件事情能办到,那么find_value不就等同于find_node么???
可是这件事情真的好实现么?
其实邻里之间互帮互助从一开始就搞起来的话,还真不叫什么事儿。总共你就160个分组,里面都有你的眼线(K个),想维护这样的关系确实不难。比如我举个例子哈,当某个节点贡献一份新的文件,那么将这个文件计算得到hash值之后,直接让这个文件所在节点去find_node,找这个哈希值的节点,不就完了么。找不到的话就找附近K个节点,不就完了么?
这样一来,这个关系不就搞起来了么!

猜你喜欢

转载自blog.csdn.net/weixin_44039270/article/details/106672769