Chord:一个用于网络应用的可扩展的P2P查询服务(下)

5 并发操作和失效

在实际应用中Chord需要处理节点同时加入系统,以及节点实效或者自行离开的情况。本节描述了对第四节基本Chord算法的修改,以处理这些情况。

5.1 稳定性

第四节描述的加入算法在网络进化时主动积极的维护所有节点的finger table。因为在大规模网络中,面对节点的并发加入的情况,这些不变性是很难维护的,我们要区分对待正确性和性能的目标。一个基本的“稳定化” (stabilization)协议,可以保持节点的后继指针始终是最新的,这对于保证查询的正确性是足够的。这些后继指针就可以被用来验证和校正 finger table表项,这可以允许这些查询不仅正确而且快速。
如果加入的节点已经影响了Chord环的一些区域,一个在稳定性结束前发起的查询将会产生下面的3种行为之一。最平常的情况就是当前查询所涉及到的 finger table表项都是合理的,这将在O(logN)步骤内找到了正确的后继。第二种情况就是后继指针是正确的,但fingers是不正确的,这也会产生正确 的查询结果,但会慢些。最后一种情况,受影响区域中的节点的后继指针是不正确的,或者key还没有迁移到新加入的节点上,查询将会失败。基于Chord的 上层软件将会发现期望的数据没有找到,有间隔一会再次查询的选项。间隔可以是短暂的,因为稳定性将会很快修复好后继指针。
我们的稳定性机制保证在Chord环中加入新节点时保持已存在将节点的可达性,即使是在节点的并发加入和离开,以及存在重新排序的消息的情况下。稳定性本 身并不会更正已经被分为多个不相交环,或者存在多次循环的单环的Chord系统。这些病态的情况并不会因为任何顺序的一组节点的加入而导致。但是并不清楚 网络分区和恢复,或者间歇性故障是否会导致这些情况。一旦出现了这种情况,可以通过周期性的环形拓扑采样来探测和修复。
图7显示了加入和稳定化的伪代码,它取代图6来处理并发加入的情况。从节点n开始,它调用n.jion(n’),其中n’是网络中的一个Chord节点。函数jion请求n’查找n的后继并返回。函数jion并没有把n的加入告诉网路中的其它节点。
每一个节点都周期性的运行stabilization(这样它们就能知道网络中的新加入节点)。当节点n运行stabilization时,它询问n的后 继s,来获取s的前驱结点p,然后决定p是否应该是n的后继(而不是s)。如果p是新加入系统中的,就可能会遇到这种情况。函数 stabilization还把n的存在通知n的后继,以使n的后继有机会把自己的前驱结点更新为n,当后继知道没有比n更近的前驱时就会更新。

  1. // n加入网络,只设置自己的后继,简单   
  2. n.jion(n’)  
  3.    predecessor = nil;  
  4.    successor = n’.find_successor(n); // RPC   
  5. // 周期性检查后继,告诉后继自己的存在   
  6. n.stabilize()  
  7.    x = successor.predecessor; // RPC查询   
  8.    if (x IN (n, successor))  
  9.       successor = x;  
  10.    successor.notify(n); // RPC     
  11. // n’认为它应该是n的predecessor,n要检查   
  12. n.notify(n’) // 叫on_notify更好些,被通知   
  13.    if (predecessor IS nil OR n’ IN (predecessor, n))  
  14.       predecessor = n’;  
  15.      
  16. // 周期性的刷新finger table   
  17. n.fix_fingers()  
  18.    // finger_t[1].node就是后继,不需要在这里刷新   
  19.    i=random index > 1 IN finger tables;  
  20.    finger_t[i].node = find_successor(finger_t[i].start);  

图7 stabilization的伪代码

一个简单的例子,假设节点n加入系统,并且ID在np和ns之间,n将会选择ns作为它的后继。节点ns在收到n的通知时将会把前驱指针更新为n。接下来 当np运行stabilization时,它将向ns询问它的前驱结点(现在是n),【译注:np并不知道n的加入,所以它的后继指针仍然指向ns】np 将会选择n作为后继。最终np将会通知n,并且n将会选择np作为它的前驱。到此为止,所有前趋和后继指针都正确了。
一旦后继指针都是正确的,find_predecessor(find_successor)就能成功。没有被设置到finger的新加入节点可能会导致 find_predecessor最初就不能找到目标【译注:这里应该是在未被其他节点finger的节点执行的查询,其finger table并不完备,因此查找应该会立即失败】,但是查找算法中的循环绝不会沿着指向新节点的后继(finger_t[1])前进,直到到达正确的前驱。 最终fix_fingers将会调整finger table中的各项,省去线性扫描的需要。
定理4 一旦一个节点可以成功的解决一次查询,那么它以后也都能成功的解决。
定理5 从最后的节点加入网络经过一段时间以后,所有的后继指针都将是正确的。
这些定理的证明依赖于不变性和终止性论证。不变性陈述了一旦节点n可以通过后继指针到达r,它将永远可以。为了证明终止性,我们考虑两个节点都认为它们有 相同的后继s的情况。这种情况下,每一个都会通知s,s最终会选择两个中离它较近的(或者其它的更近的节点)作为它的前驱。于是将来通过联系s,它们能知 道一个比s更合适的后继【译注:更近】。这表明,随着时间的前进,每个节点也都是朝着更好的后继在前进。这种进展将最终停止在一个状态上,每个节点都被另 外的唯一一个节点选为后继,这就定义了一个环(或一组环,但是不变性保证了最多只有一个环)。
到目前为止,我们还没有讨论节点加入时fingers的调整,因为看起来加入并不会从根本上破坏fingers的性能。如果一个节点每个区间都设置了一个 finger,即使在有节点加入之后这些finger依然是可用的。距离的1/2递减论据从根本上没有改变,表明O(logN)跳已经足够到达一个距离查 询的目标“近”的节点了。新加入节点对查询的影响仅仅是在当前查询目标的旧前驱和后继间增加了一次查询操作。
【译注:这里说明一下,其实是下面所示的情况,考虑本来要查找id的前驱为节点p,后继为节点s,n为新加入的节点,假设后继指针都是正确的,否者查找将失败,此时s执行find_successor(id),如下图所示:】

【译注:因为p在s的finger中,因此s的find_closed_finger返回p。如果没有新加入的n,那么p的后继为s,于是查询结束 返回节点s。n加入后,p.successor=n,因此测试id不在(p, n)区间内,于是p继续从自己的finger table中得知n属于区间(p, id),find_closed_finger返回n,n测试id属于区间(n, n.successor),于是最终返回s,查找结束。可见由于n的加入,在p和s之间多了一次查找过程。】
这些新节点可能需要先行扫描(如果它们的fingers并不正确)。但是除非是大量的节点同时加入系统,两个已有节点之间的节点数据是很小的,因此对查找的影响可以忽略不计。我们有下面的定理:
定理6 如果我们有一个N节点的稳定网络,另外有N个没有正确fingers的节点(但是有正确的后继)加入到网络中,在很大概率上查询时间依然是O(logN)。
一般地说,只要调整fingers所需要的时间比把网络规模扩种一倍的时间少,查询时间都应该是O(logN)。

5.2 失效和复制

当节点n失效时,finger table中包含n的节点必须查找n的后继。此外,n的失效必须不能干扰正在进行的查询,因为系统正在重建稳定性。
失效恢复的关键步骤在于维护正确的后继指针,因为在最坏情况下,find_predecessor将仅使用后继指针向下传播。为了达到这个目标,每个 Chord节点都为在Chord环上距离最近的r个后继维护一个“successor-list”(后继列表)。在一般操作中,图7描述的 stabilize函数的改进版会维护successor-list。如果节点n发现它的后继失效了,它将从successor-list中取出第一个活 动后继来代替。在那个时刻,节点n可以把查询从失效的后继转到新的后继上。随着时间流逝,函数stabilize将对finger table和successor-list中指向失效节点的项进行更正。
在节点失效之后,但稳定化完成之前,作为find_successor查询中的一步,其它节点可能会尝试通过失效的节点发送请求。理想的情况是尽管有错 误,但是一段时间后查询可以经过其他路径继续下去,很多情况下这都是可能的。所需的就是一组替换节点,这很容易从finger table中在失效节点之前的表项中找到。如果失效节点的finger table索引很小,也可以用successor-list中的节点替换。
技术报告证明了下面的两个定理,它表明了successor-list使查询成功而有效,即使在稳定化期间[21]。
定理7 如果我们在一个具有稳定初态的网络中使用长度r=O(logN)的successor-list,且每个节点失效的有概率为1/2,那么在很大概率上find_successor将返回距查询key最近的活动后继。
定理8 如果我们在一个具有稳定初态的网络中使用长度r=O(logN)的successor-list,且每个节点失效的有概率为1/2,那么在该失效网络中执行find_successor的期望时间是O(logN)。
直觉上这些定理的证明是很直观的:一个节点的r个后继都失效的概率是2^-r = 1/N,因此在很大概率上一个节点可以意识到并能把消息传递给它最近的活动后继。
另外,successor-list机制也能帮助上层软件复制数据。一个典型的使用Chord的应用可能会把一个key关联的数据复制到该key的k个后 继结点上。Chord节点记录它的r个后继的事实意味着,当后继加入、离开时它可以通知上层应用,于是软件可以扩增新的复制。

6 仿真和实验结果

本节中,我们通过仿真评估Chord协议。仿真器使用图4中的查询算法和图5中较旧的稳定化算法。我们还汇报了运行在网络主机上基于Chord的可运行系统的初步试验结果。

6.1 协议仿真

Chord协议可以采用迭代或者递归的方式实现。在迭代方式中,发起查询的节点发起所有的通信:它从自己的finger table中查询一系列节点的信息,在Chord环上向目标后继逐步靠近。在递归方式中,每个中间节点都向下一个节点转发请求,直到到达目标后继。协议仿 真采用了迭代模式。

6.2 负载平衡

我们首先考虑一致性hash算法平均分配key给node的能力。在一个N个节点和K个key的系统中,我们希望key在节点中的分布是N/K的紧界。
考虑有10^4个节点的网络,key数目以10^5为单位从10^5递增到10^6,对每个值重复试验20次。图8(a)绘制了每个节点上的平均和第1和 99个百分位数的key数目。节点上的key数目表现出了很大的变动,并且随着key数目增加。比如在所有用例中,有些节点上没有key。为了阐明这一 点,图8(b)绘制了网络中有5 x 10^5个key时,节点上key数目的PDF函数(Probability Density Function,概率密度函数)。在这种情况下,任何节点存储的最大key数目是457,即9.1x平均值,作为对比,第99个百分点是4.6x平均 值。
原因之一是节点标识符并没有均衡的覆盖整个标识符空间。如果我们把描述符空间分成N个固定大小的区域,其中N是节点个数,我们期望每一个区域包含一个节 点。然而实际上,一个区域没有任何节点的概率是(1 – 1/N)^N,对于足够的N,这个值是e^-1 = 0.368。

 
图8 (a)包含10^4个节点的网络中,平均、第1和99个百分点的key个数/节点
(b)节点上key的PDF,总结点数为5x10^5

 

上面已经讨论过,一致性hash算法论文采用了虚拟节点来解决此问题,把每个真实节点映射为 v个标识符不相关的虚拟节点,直观上这会是分配更加均衡。比如我们选择v=logN,那么很大概率上N个区域每个都包含O(logN)个节点[16]。注 意到这并不会影响最差情况下的查询路径长度,现在变成了O(log(NlogN)) => O(logN)。
我们做实验来验证这个假设,这种情况下,key被关联到了虚拟节点而不是实际节点上。依然考虑一个包含10^4到10^6个节点的网络,图9分别显示了 r=1, 2, 5, 10和20时的第1和99百分点。如预期的那样,随着虚拟节点个数r的增加,第99个百分点下降而第1个百分点上升了。特别的,第99个百分点从4.8x 降低到了1.6x平均值,而第1百分点则从0增长到了0.5x平均值。因此使用虚拟节点可以有效地改善负载平衡性。权衡就是会增加路由表信息空间,因为节 点现在需要r倍的空间来为其虚拟节点存储路由信息。然而,我们相信实际上这种空间的增加很容易就能满足。比如假设一个N=10^6的网络,r=logN, 那么每个节点仅需要维护logN*logN约400个表项的路由表。


图9 使用虚拟节点后的节点分布情况,网络中有10^4个节点和10^6个key

6.3 路径长度

任何路由协议的性能都严重依赖网络中任意两个节点的路径长度。在Chord语境中,我们把路径长度定义为一次查询操作所经历的节点数。由定理2,在很大概率上,解决一次查询的路径长度为O(logN),N为网络的节点数。
为了获悉Chord的实际路由性能,我们使用一个N=2^k,key数目为100x2^k的网络进行仿真,对从3到14的不同K值分别进行实验。每次试验中都从每个节点随机选择一组key查询,我们测试每次查询的路由长度。
图10(a)绘制了k的第1和99个百分点的路径长度函数。如预期的那样,平均路径长度随着节点数呈对数级增长。图10(b)绘制了有2^12个节点(k=12)的网络的路径长度的PDF。

图10(a)k为输入的路径长度函数 (b)具有2^12个节点的网络的路径长度PDF


图10(a)显示出平局路径长度约为0.5*O(logN),原因如下。考虑一些随机节点和一次随机查询,以二进制来表示标识符空间中的距离。沿着节点的 第i个finger,距离中的首位bit(第i个)能被纠正为0。如果距离中的第二个bit是1,也将被下一个finger更正为0,如果是0,那么不会 沿着第i-1个finger向下,而是开始更正第i-2个bit。大体上,我们需要跟随的finger数将会是从节点到查询目标距离的二级制表示中1的个 数。因为距离是随机的,因此其中有logN个bit可能为1。

6.4 节点失效仿真

该实验中,我们评估Chord在大面积节点失效后重新恢复一致性的能里。依然考虑10^4个 节点和10^6个key的网络,并随机选择p份节点失效。失效发生后,我们等到网络稳定化结束,测量不能被正确查询key的比例。一次对key的正确查询 就是找到失效前原本负责该key的节点,这对应于一个存储key和value的系统,但是没有存储value的副本或者没有在失效后恢复value。


图11 查询失效函数


图11绘制了查询平均失效比例和第95%的置信区间(p的函数)。查询失效比例几乎就是p,因为这就是因为节点失效而导致的key丢失的比例,我们推断出 Chord网络中没有重大的查询失效。比如,如果Chord网络划分成了两个对等的部分,可以预测一半的请求将实效,因为查询节点和目标可能在两个分区 中。我们的实验没有表现出这样的结果,表明了Chord在面临节点同时失效时仍然是健壮的。

6.5 稳定化期间的查询

一个在失效和稳定化结束之间发起的查询可能因为两个原因失败。首先,负责key的节点可能失效了。第二,在节点并发加入和失效时,某些节点的finger table或者predecessor指针可能不一致。本节评估了持续的节点加入和失效对查询的影响。
实验中,如果查询到达了key的后继,就认为是成功的。这有些理想化,在实际系统中,可能在一段时间内,key的后继还没有把key相关的数据从前一个后 继中接收过来。然而,这个方法让我们集中在测试Chord本身的查询能力,而不是上层软件维护数据一致性的能力上。任何失败的查询都是Chord不一致的 结果。另外,仿真没有查询重试机制:如果一个查询被转发到失效的节点上,查询就失败了。因此本节得出的结果可以看作是由于状态不一致而导致的查询失败的最 差情况。
因为不一致(inconsistency)的主要根源在于节点的加入和离开,且解决这些不一致的主要机制是稳定化规则,对于节点的频繁加入和离开VS稳定 化规则被调用的频率,Chord的性能是敏感的。该实验使用rate为1的泊松过程生成节点的查询key,加入和离开由平均到达速率为R的泊松过程建模, 每个节点以一个随机的间隔运行稳定化程序,平均间隔为30s。和图7中的程序不同,仿真器每次调用都更新所有的finger table。网络从500个节点开始。

图12 根据节点加入、失效概率的查询失效比例
仅包含Chord不一致性造成的失效,不计key丢失的原因

图12绘制了平均失效概率和置信区间,0.01的节点失效概率对应于平均每100s有一个节 点加入或离开,每个节点30s调用一次稳定化程序。换句话说,图中x轴的范围是[1失效概率/3稳定化步骤, 3失效概率/1稳定化步骤]。图12展现的是平均大约2个小时的结果。置信区间是根据10次独立运行计算出来的值。
图12中的结果可以粗略解释如下。仿真使用500个节点,平均查询路径长度为5左右。如果finger路径遇到失效节点查询将会失败。如果k个节点失效, 路径中任一个节点是失效节点的概率大约是5*k/500=k/100。这意味着如果稳定化期间有3个节点失效,那么失效概率约为3%。图中用圆点显示,结 果稍微略差,这是因为完全清除一个失效节点可能需要不止一次的稳定化操作。

6.6 试验结果

本节介绍部署在因特网上的Chord原型的时延测量结果。Chord节点在分别位于 California、Colorado、Massachusetts、New York、North Carolina、和Pennsylvania的10个RON测试平台上。Chord软件运行在Unix平台上,采用了迭代模式,使用SHA-1加密 hash函数产生的160bit的key,节点之间通过TCP通信。这些Chord节点是实验性分布式文件系统的一部分[7],尽管本节仅描述了系统的 Chord组件。


图13 网络原型的查询时延,每个节点都运行多个独立的Chord软件

图13显示了经过一系列节点的Chord查询时延。实验节点超过10个,每个节点上同时运行多个独立的Chord软件。是为了测试即使只有少数节点时的扩展性,这和为了获取负载平衡性能而使用O(logN)个虚拟节点不同。
对于图13所示的节点数,每个物理设备逐个查询16个随机选择的key。图中描绘了平均、第5和95个百分点的查询时延。平均时延分布在 180-250ms,跟节点数量相关。对于180个节点的用例,一个典型的查询涉及到5个双向消息交换:4个查询和1个最终结果。设备之间的典型往返时间 时延是60ms(根据ping测量),因此180个节点的期望查询时间为300ms(5x60ms),接近平均时延285ms。时延最低的5个百分点是由 接近查询key的目标节点,以及在物理设备的本地查询跳数引起的。时延最高的5个百分点是因为跳数沿着高时延路径的查询引起的。
图13表明查询时延随着节点个数缓慢增加,这验证了Chord扩展性的仿真结果。

7 将来的工作

基于在6.6节提到的原型上的实验,我们希望在以下几个方面改进Chord的设计。
当前Chord不具备消除分割环的机制,这些环可能在稳定化过程中显示出局部一致性。检查全局一致性的一种方法是让每个节点n周期性的请求其它节点查询 n,如果没有查到n则表明可能出现了分割环。这只能用在节点之间互相熟悉的网络中,获得这些知识的一个方法就是让每个节点都保存一组相同的初始节点。另一 个方法是节点保存他们过去遇到的一组随机节点,如果出现分割,那么这些节点很可能会分布在不同的分区上。
一些恶意的或者有bug的Chord参与者可能会呈现不正确的Chord环。设想Chord正在使用的位置是加密认证的,这是一个对可用性而非真实性的威胁。上面用到的检测分隔的方法可能会帮助到受害者认识到他们没有看到Chord环的全局一致性。
一个攻击者可能通过在Chord环上插入一个节点来攻击一条数据,该节点的ID紧跟着数据的key,并让该节点在被请求查询该数据时返回错误。要求(或检查)节点的ID取自IP地址的SHA-1值,将使攻击变得困难。
对于某些Chord应用而言,每次查询需要O(logN)的信息也还是太多了,特别是如果每个消息都必须发送到一个任意的网络主机时。Chord可以选择 以(1+1/d)^i代替2^i的间隔距离来设置finger,这很容易做到。在这种机制下,一跳路由的查询距离将减为原来的1/(1+d),于是只要 log(1+d)N的跳数。然而所需的finger将会增加到N/log(1+1/d)=>O(d*logN)。
改进查询时延的另一个不同的方法是使用服务器选择机制。每个finger table表项存储都对应Chord环区间上的前k个节点,节点可以测量和这k个节点的时延。对于查询而言这k个节点都是平等的,因此节点可以把请求转发 到具有最小时延的节点上。这个方法对递归Chord查询是最有效的,因为测试时延和转发查询请求的是同一个节点。

8 总结

大多数P2P应用都需要定位存储一个数据项的节点。Chord采用了去中心化的方式来解决这 一问题。Chord提供了一个强大的起点:给定一个key,它能有效的决定存储key关联数据的节点。稳定状态下,在一个N节点的网络中,每个节点仅需维 护O(logN)的路由信息,并通过和其他节点交互O(logN)的信息来解决所有的查询。在节点加入、离开时仅需要O(logN*logN)的信息来更 新路由信息。
Chord的吸引人之处包括简单、已证明为正确的和已证明为有效的,即使面对节点的并发加入和离开时。当一个节点的信息是部分正确时,即使性能优雅的降 级,它仍能够正常工作。我们的理论分析、仿真和实验结果都证明了Chord能够随着节点个数扩展,能从大规模的节点并发失效和加入中恢复,即使在恢复时也 能正确的解决大多数查询。
我们相信Chord会成为P2P系统、大规模分布式系统的有价值的组成部分, 比如协同文件共享、时间共享存储系统、文档和服务查询的分布式索引和大规模分布式计算平台。

答谢

感谢Frank Dabek对6.6小节描述的Chord原型的测试,感谢David Andersen在这些环境中搭建测试平台。

9 参考文献

这个不贴了,参考原文吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值