基于Chord的结构化P2P平台

来源:互联网 发布:农村淘宝招募令 编辑:程序博客网 时间:2024/05/02 14:56

基于Chord的结构化P2P平台  

  Chord协议阐述了怎样找到给定值的存储位置,新结点怎么样加入系统以及怎么样从已有结点的失败(或离开)中恢复。Chord的核心在于提供了一个快速的分布式的Hash功能计算,把值映射到存储它们的结点上去。Hash功能可以平衡负载,而且,当第N个结点加入(离开)网络时,只有O(1/N)部分值被移到了不同的地方。Chord可以使得每个结点并不需要知道其他所有的结点,改进了连续Hash的可扩展性。一个Chord结点只需要一小部分关于其他结点的路由信息。因为这些信息都是分布式的。一个结点需要通过与一些结点的通信来解析Hash函数的。在稳定状态下,在一个有N个结点的系统中。每个结点只需要维持O(logN)条关于其他结点的信息,解析所有的搜索也只需要O(logN)条信息。
为了搭建这一平台,实现Chord层的结点路由以及定位。本文设计了如下六个类:
1.    doc类:该类主要描述了所有关于文档的操作。实现了文档的生成,查找,插入,删除,移动等功能。
2.    event类:该类主要描述了事件及事件栈的操作。实现了事件栈的初始化,新事件的生成,删除,插入,返回。
3.    node类:该类主要描述了所有关于网络中结点的操作以及连续hash函数的构造。实现了新结点的加入,删除,打印以及哈希表的构造和结点随机返回等功能。
4.    finger类:该类主要描述了结点finger表的相关操作。实现了结点finger表的创建,删除,更新,打印以及寻找结点前驱,后继结点等功能。
5.    request类:该类主要实现了新请求创建,插入挂起列表,请求处理,打印请求列表,复制后继结点的finger表等功能。
6.    fun类:该类实现了结点的加入,离开以及因此带来的网络自我调整等功能。
上述类中具体算法实现请参见程序部分,在接下来的几节中,作者将描述一个基本的Chord协议,包括了连续hash函数的构建,键值的搜索和结点的加入,离开等。

3.3.1 连续Hash函数
     连续的hash函数为每个结点以及值分配一个m位的标识符。哈希结点的IP地址得到结点的标识符,同样的,哈希关键字可以得到值的标识符。其中,标识符长度m必须确保两个结点或者值被哈希到同一个标识符的可能性可以忽略不计。
Hash函数按照如下规则将值分配给结点:标识符被有秩序地分配在一个模2^m的标识符圆环上。键值K被分配到第一个在标识符空间内的ID值是大于等于K的结点。这个结点被称之为键值K的后继结点,表示为successor(K),即Chord环上从K顺时针出发第一个被访问到的结点。下图为m=6,有10个结点以及5个值的Chord环。当一个结点n加入或离开网络中时,某些值会重新进行分配,从而达到网络的自适应。
            
 一个拥有10个结点的chord环事实上,对于任何一个hash函数,总有一些值没有被哈希函数分散得很开,但在实际应用中这些潜在的所谓坏集出现的可能性不大。在哈希函数中引入随机性的技术已经大大的提高了,给定一个值的集合,我们可以找到一个随机的哈希函数,使得这些值能够尽可能的分开。对于标准的哈希而言,任意一个非针对性的值的集合都能被分析,在这里,我们就把它们当作是随机的。
实现该功能的相关函数原型如下:
void initNodeHashTable()
/*初始化结点哈希表*/
Node *addNode(int id)
/*在哈希表中加入指定结点信息*/
void deleteNode(Node *n)
/*在哈希表中删除指定结点信息*/
Node *getNode(int id)
/*返回哈希表中的结点信息*/
int getRandomActiveNodeId()
/*随机生成动态结点ID*/
int getRandomNodeId()
/*返回一个随机结点*/
3.3.2 键值搜索
这一部分主要介绍一种有效的并能准确维持路由信息的Chord搜索算法。如前所述,假设key/node对的标识符为m位。每个结点n拥有一张最多有m条记录的路由表,称之为finger表。结点n表中的第i项包含了与n的id至少相差2^i-1的第一个节点s,换言之,s=successor(n+2^i-1),此处的i要满足1< i < m,s表示为n.figher[i]。finger表中的每一项都包含了结点在Chord网中的id以及对应的真实IP地址。
            
  结点N8的finger表利用finger表进行键值搜索的主要算法如下:如果需要查找的值的id正好落在n以及n 的后继结点之间,find_successor操作完成,返回其后继结点。否则,n就搜索他finger表中最靠近这个id的结点m,然后递归调用find_successor函数,直到找到值id的前驱结点为止。

实现该功能的具体算法如下:

Node find_successor(Node n, ID id)
{ Node m; 
if(id>node.id&&id<=node.successor.id)
return successor;
else
{
m=closest_preceding_node(n, id);
return find_successor(m, id);
}
Node closest_preceding_node(Node n, ID id)
/*查找n的finger表中最靠近指定id的节点*/
{
for(int i=n; i>0; i++)
{
if(n.finger[i]>n.id&&n.finger[i]<id)
return n.finger[i];
}
return n;
}