4000-520-616
欢迎来到免疫在线!(蚂蚁淘生物旗下平台)  请登录 |  免费注册 |  询价篮
主营:原厂直采,平行进口,授权代理(蚂蚁淘为您服务)
咨询热线电话
4000-520-616
当前位置: 首页 > 新闻动态 >
新闻详情
P2P网络Chord环的构造_日西月东的专栏-CSDN博客
来自 : CSDN技术社区 发布时间:2021-03-24

最近上网络服务系统的课 对于一个只具备基本的TCP/IP知识的开发人员来说 算是新知识了。只是老师上课照本宣科 真心没听明白前因后果。于是面对下面这道复习题 只好自己到网上找资料了。

先贴题目吧

画出m 4的带弦Chord环的示意图 图中实际存在的节点NodeID分别为0、3、11、14 试
1 给出每个节点的finger table
2 描述NodeID为5的新节点从节点11发起加入网络的过程

网上有一篇极好的资料 摘录如下

P2P的一个常见问题是如何高效的定位节点 也就是说 一个节点怎样高效的知道在网络中的哪个节点包含它所寻找的数据 如下图

\"这里写图片描述\"

对此 有三种比较典型的来解决这个问题。

Napster 使用一个中心服务器接收所有的查询 服务器告知去哪下载其所需要的数据。存在的问题是中心服务器单点失效导致整个网络瘫痪。

\"这里写图片描述\"

Gnutella 使用消息洪泛 message flooding 来定位数据。一个消息被发到系统内每一个节点 直到找到其需要的数据为止。当然 使用生存时间 TTL 来限制网络内转发消息的数量。存在的问题是消息数与节点数成线性关系 导致网络负载较重。

\"这里写图片描述\"

SN型 现在大多数采用所谓超级节点 Super Node SN保存网络中节点的索引信息 这一点和中心服务器类型一样 但是网内有多个SN 其索引信息会在这些SN中进行传播 所以整个系统的崩溃几率就会小很多。尽管如此 网络还是有崩溃的可能。

现在的研究结果中 Chord、Pastry、CAN和Tapestry等常用于构建结构化P2P的分布式哈希表系统 Distributed Hash Table DHT 。

DHT的主要思想是 首先 每条文件索引被表示成一个(K, V)对 K称为关键字 可以是文件名 或文件的其他描述信息 的哈希值 V是实际存储文件的节点的IP地址 或节点的其他描述信息 。所有的文件索引条目(即所有的 K, V 对)组成一张大的文件索引哈希表 只要输入目标文件的K值 就可以从这张表中查出所有存储该文件的节点地址。然后 再将上面的大文件哈希表分割成很多局部小块 按照特定的规则把这些小块的局部哈希表分布到系统中的所有参与节点上 使得每个节点负责维护其中的一块。这样 节点查询文件时 只要把查询报文路由到相应的节点即可 该节点维护的哈希表分块中含有要查找的(K,V)对 。

这里介绍的Chord算法就是解决网络内节点定位问题的一种P2P协议。它通过多个节点跳转找到我们所查找的资源

\"这里写图片描述\"

我们先看看它是如何进行的 随后再总结其特点和操作特征 以及一些实现。

1.Chord里面的基本要素

节点ID NID node identifier 表示一个物理机器 m位的一个数字 m要足够大以保证不同节点的NID相同的几率小的可以忽略不计 由节点机器的IP地址通过哈希操作得到。

资源ID KID key identifiers 原为键ID 其实际表示一个资源 因为Key与一个资源value哈希绑定 故在本文中统称资源ID 这样比较直观 m位的一个数字 m要足够大以保证不同资源的KID相同的几率小的可以忽略不计 由Key通过哈希操作得到。

常哈希函数 较之一般哈希函数 节点的加入和离开对整个系统影响最小 另外还有一些优势在此不赘述。在Chord中使用SHA-1来进行常哈希计算。

Chord环 Chord Ring NID和KID被分配到一个大小为2^m的环上 用于资源分配 给某一个节点 和节点分布 以及资源定位 注 在这个环上的ID为0–2^m-1 。首先我们说资源分配 资源被分配到NID KID的节点上 这个节点成为k的后继节点 是环上从k起顺时针方向的第一个节点 记为successor(k)。而节点分布则顺时针将节点N由大到小放在这个环上。例如下边这幅图

\"这里写图片描述\"

这是一个m 6的环 其中有10个节点 5个资源 K10的后继节点为N14 也就是说K10被分配给了N14。

2.Chord资源定位 Key Location

资源定位是Chord协议的核心功能 为了便于理解 我们先介绍一个简单的资源定位方法 然后再介绍这个可伸缩的资源定位方法。

简单方法

考虑如下场景 节点n寻找KID为id的资源 此时节点n首先问询是否在下一个节点上 find_successor 这要看资源k的KID是否在该节点NID和下一个节点的NID之间 若在则说明资源k被分配给了下一个节点 若不在则在下一个节点上发起同样的查询 问询下下一个点是否有该资源。如此迭代下去 用伪代码定义这个操作

n.find_successor(id) if (id є (n; successor]) return successor; else // 将查询沿着环进行下去 return successor.find_successor(id);

例如下图

\"这里写图片描述\"

节点N8寻找K54这个资源 N8.find_successor(K54)发现下一个节点N14不合符54є (8; 14] 于是N14发起同样的搜索 然后一跳一跳后直到节点N56满足54є (51; 56] 于是得知资源K54在N56这个节点上。

在一个有N个节点的环上 这样的查找方法显然在最坏的时候要查找N次才能得到所需资源的位置 查找次数与节点个数成线性关系。显然 这样的效率不给力 所以Chord使用了可伸缩资源定位的方式来提高效率。

可伸缩方法

在每个节点N上都维护了最多有m项 m为ID的位数 的路由表 称为finger table 用来定位资源。这个表的第i项是该节点的后继节位置 至少包含到2^(i-1)后的位置。还是延续上边的例子

\"这里写图片描述\"

节点N8的路由表中 左边那一栏包含了N8 1到N8 32 2^5-1 的位置 右边那一栏每个位置对应的实际存在的节点。比如N8 1-N14 表示在N8后的第一个位置上的资源由N14来负责。这样记录有以下优势

每个节点只包含全网中一小部分节点的信息。

每个节点对于临近节点负责的位置知道的更多 比如N8节点对于N14负责的位置知道3处 而对N21负责的位置只知道1处。

路由表通常不包含直接找到后继节点的信息 往往需要询问其他节点来完成。

当在某个节点上查找资源时 首先判断其后继节点是不是就持有该资源 若没有则直接从该节点的路由表从最远处开始查找 看哪一项离持有资源的节点最近 发现后跳转 若没有则说明本节点自身就有要寻找的资源。如此迭代下去。

例如 节点N8寻找K54这个资源

\"这里写图片描述\"

首先 在N8上查找后继节点为N14 发现K54并不符合54є (8; 14]的要求 那么直接在N8的路由表上查找符合这个要求的表项 由远及近查找 此时N8的路由表为

\"这里写图片描述\"

我们发现路由表中最远的一项N8 32–N42满足42є (8; 54] 则说明N42这个点离持有K54这个资源的节点最近 因为N42在该路由表中离N8这个节点最远 那么此时跳到N42这个节点上继续查找。N42的后继节点为N48 不符合54є (42; 48]的要求 说明N48不持有资源54 此时 开始在N42的路由表上查找

N42节点的路由表为

\"这里写图片描述\"

我们由远及近开始查找 发现N42 8–N51满足51є (42; 54] 则说明N51这个点离持有K54这个资源的节点最近 那么此时跳到N51这个节点上继续查找。N51节点的后继节点为N56 符合54є (51; 56] 此时定位完成 N56持有资源节点K54。

用伪代码表示

// 查询节点n后继节点。 n.find_successor(id) if (id є (n; successor]) return successor; else n0 closest_preceding_node(id); return n0.find successor(id); // search the local table for the highest // predecessor of id n.closest_preceding_node(id) for i m downto 1 if (finger[i] є (n; id)) return finger[i]; return n;

经证明 最多经过O(log N)次查找就能找到一个资源。

3.Chord的节点加入

Chord通过在每个节点的后台周期性的进行stabilization询问后继节点的前序节点是不是自己来更新后继节点以及路由表中的项。

有三个操作
join(n0) n加入一个Chord环 已知其中有一个节点n0.

Stabilize(): n查询其后继节点的前序节点P来决定P是否应该是n的后续节点 也就是说当p不是n本身时 说明p是新加入的 此时将n的后继节点设置为p。

Notify(n0): n0通知n它的存在 若此时n没有前序节点或 n0比n现有的前序节点更加靠近n 则n将其设置为前序节点。

Fix_fingers(): 修改路由表。

具体的 例如

这是原先的结构

\"这里写图片描述\"

现在N26节点要加入系统 首先它指向其后继N32 然后通知N32 N32接到通知后将N26标记为它的前序节点 predecessor 。如下图

\"这里写图片描述\"

然后N26修改路由表 如下图

\"这里写图片描述\"

下一次N21运行stabilize()询问其后继节点N32的前序节点是不是还是自己 此时发现N32的前序节点已经是N26

\"这里写图片描述\"

于是N21就将后继节点修改为N26 并通知N26自己已经将其设置为后继节点 N26接到通知后将N21设置为自己的前序节点。

这个加入操作会带来两方面的影响

1)正确性方面 当一个节点加入系统 而一个查找发生在stabilization结束前 那么此时系统会有三个状态

A.所有后继指针和路由表项都正确时 对正确性没有影响。

B.后继指针正确但表项不正确 查找结果正确 但速度稍慢 在目标节点和目标节点的后继处加入非常多个节点时 。如下图

\"这里写图片描述\"

C.后继指针和路由表项都不正确 此时查找失败 Chord上层的软件会发现数据查找失败 在一段时间后会进行重试。

总结一下 节点加入对数据查找没有影响。

2)效率方面 当stabilization完成时 对查找效率的影响不会超过O(log N) 的时间。当stabilization未完成时 在目标节点和目标节点的后继处加入非常多个节点时才会有性能影响。可以证明 只要路由表调整速度快于网络节点数量加倍的速度 性能就不受影响。

看完这些介绍 你可能会会心一笑吧 这和双向链表的元素插入何其相似 :-) 只不过加上了一些网络上特有的信息罢了。

回到最开始的那道题 也就很好解了。直接贴拍照图了 有空的话再用工具画画吧。
\"这里写图片描述\"

\"\" \"\" \"\" 点赞 5 \"\" \"\" 评论 2

本文链接: http://chordip.immuno-online.com/view-694035.html

发布于 : 2021-03-24 阅读(0)