- keyspace 即ID有多少位,决定每个节点的通讯录有几层
- k 每个一层k-bucket里装k个node的信息,即<node ID, IP Adress, port> 每次查找node时,返回k个node的信息,对于某个特定的data,离其key最近的k个节点被会要求存储这个data
- α 每次向其他node请求查找某个node时,会向α个node发出请求
Kademlia算法中,每个节点只有4个指令
- PING 测试一个节点是否在线
- STORE 要求一个节点存储一份数据
- FIND_NODE 根据节点ID查找一个节点
- FIND_VALUE 根据KEY查找一个数据,实则上跟FIND_NODE非常类似
- nodeId 节点ID
- ip,port ip地址和端口
- data 数据
- k桶 路由表
k桶的排布
根据nodeId间的异或距离排布,比如
节点1111的k桶排布
k1 1110 最后一位不同
k2 1101 1100 最后第二位不同
k3 1011 1010 1001 1000 最后第三位不同
k4 0111 0011 0001 0000 0101 0100 0110 0010
k桶的更新
- 主动收集节点:任何节点都可以发起四个命令,从而从返回值刷新K-桶中的节点信息
- 被动收集节点:当收到其他节点发送过来的请求(如:FIND_NODE、FIND_VALUE),会把对方的节点ID加入到某个K-桶中
- 检测失效节点:通过发起PING请求,判断K-桶中某个节点是否在线,然后清理K-桶中那些下线的节点
当一个节点ID要被用来更新对应的K-桶,其具体步骤如下:
- 计算自己和目标节点ID的距离d
- 通过距离d选择路由表中对应的K-桶,如果目标节点ID已经在K-桶中,则把对应项移到该K-桶的尾部
- 如果目标节点ID不在K-桶中,则有两种情况:
- 如果该K-桶存储的节点小于K个,则直接把目标节点插入到K-桶尾部;
- 如果该K-桶存储节点大于等于K个,则选择K-桶中的头部节点进行PING操作,检测节点是否存活。如果头部节点没有响应,则移除该头部节点,并将目标节点插入到队列尾部;如果头部节点有响应,则把头部节点移到队列尾部,同时忽略目标节点。
当一个节点要加入网络
前提:必须知道一个存在于网络中的有效节点
具体步骤:假设节点A是待加入节点,节点B是节点A知道的网络中的有效节点,有如下流程
- 节点A把节点B加入到自己的k桶
- 节点A生成一个随机的ID值,知道离开网络,节点A一直用这个ID作为自己的nodeId
- 计算与节点B的距离,并向距离最近的α个节点(开始只有B节点)发起FIND_NODE请求,请求定位的节点是自己的节点ID
- 其他节点在收到节点A的FIND_NODE请求后,会根据FIND_NODE请求的约定,找到K个距离A最近的节点,并返回给A节点,并把A节点加入自己的k桶
- A收到这些节点以后,就把它们加入到自己的K-桶中,并再进行步骤3,直到距离没有更新
具体步骤
- 首先由发起者确定目标ID对应路由表中的K-桶位置,然后从自己的K-桶中筛选出K个距离目标ID最近的节点,并同时向这些节点发起FIND_NODE的查询请求
- 被查询节点收到FIND_NODE请求后,从对应的K-桶中找出自己所知道的最近的K个节点,并返回给发起者。
- 发起者在收到这些节点后,更新自己的结果列表,并再次从其中K个距离目标节点ID最近的节点,挑选未发送请求的节点重复Step1步骤
- 上述步骤不断重复,直到无法获取比发起者当前已知的K个节点更接近目标节点ID的活动节点为止。 在查询过程中,没有及时响应的节点应该立即排除,同时查询者必须保证最终获得的K个节点都是在线的。
当节点要查询<key, value>数据对时,和定位节点的过程类似
- 首先发起者会查找自己是否存储了<key, value>数据对,如果存在则直接返回,否则就返回K个距离key值最近的节点,并向这K个节点ID发起FIND_VALUE请求
- 收到FIND_VALUE请求的节点,首先也是检查自己是否存储了<key, value>数据对,如果有直接返回value,如果没有,则在自己的对应的K-桶中返回K-个距离key值最近的节点
- 发起者如果收到value则结束查询过程,否则发起者在收到这些节点后,更新自己的结果列表,并再次从其中K个距离key值最近的节点,挑选未发送请求的节点再次发起FIND_VALUE请求
- 上述步骤不断重复,直到获取到value或者无法获取比发起者当前已知的K个节点更接近key值的活动节点为止,这时就表示未找到value值。
如果上述FIND_VALUE最终找到value值,则<key, value>数据对会缓存在没有返回value值的最近节点上,这样下次再查询相同的key值时就可以加快查询速度。 所以,越热门的资源,其缓存的<key, value>数据对范围就越广。这也是为什么我们以前用P2P下载工具,下载的某个资源的人越多时,下载速度越快的原因。
当节点收到一个<key, value>的数据时,它的存储过程如下:
- 发起者首先定位K个距离目标key值最近的节点
- 然后发起者对这K个节点发起STORE请求
- 接收到STORE请求的节点将保存<key, value>数据
- 同时,执行STORE操作的K个节点每小时重发布自己所有的<key, value>对数据
- 最后,为了限制失效信息,所有<key, value>对数据在发布24小时后过期。 ##未完待续