Chord:一個用於網路應用的可擴充的P2P查詢服務(上)

來源:互聯網
上載者:User
文章目錄
  • 4.1 概述
  • 4.2 一致性hash演算法
  • 4.3 可擴充的key定位
  • 4.4 節點加入
Chord:一個用於網路應用的可擴充的P2P查詢服務

Ion Stoica*, Robert Morris, David Karger, M. Frans Kaashoek, Hari Balakrishnan
MIT Laboratory for Computer Science chord@lcs.mit.edu

http://pdos.lcs.mit.edu/chord/

摘要

P2P(peer-to-peer)系統面臨的一個根本問題就是如何有效定位到儲存特定資料項目的節點。本文提出了Chord,一個分散式查詢協議來解決這個問題。Chord專為一種操作提供支援:給定一個key,它將key映射到對應的節點上。基於Chord,通過把key和每個data item(資料項目)關聯起來,並把該key/data item對儲存到key映射到的節點上,很容易就可以實現資料定位。Chord可以有效適應節點加入、離開系統,並且可以在系統持續變動的狀態下應答查詢。理論分析、類比和實驗結果表明,Chord是一個可擴充的協議,並且通訊代價和每個節點狀態資訊維護的代價都是系統中Chord節點個數的對數。

1 介紹

P2P系統和應用都是沒有中心控制節點或者層次化組織圖的分布式系統,系統中的節點在功能上都是相同的。近來,許多P2P應用都具有冗餘儲存(redundant storage)、持久化(permanence)、臨近伺服器選擇(selection of nearby servers)、匿名訪問(anonymity)、搜尋(search)、認證( authentication)和分級命名(hierarchical naming)等許多特徵。然而絕大部分P2P系統中,最核心的操作是高效的資料定位。本文的貢獻就在於提出了一個能為節點頻繁加入、離開的動態P2P系統提供有效查詢操作的可擴充協議。
【譯註:資料定位(data location)、查詢(query、lookup)都是一回事】
實際上Chord協議僅支援一種操作:把一個給定的key映射到一個節點(node)上。 依據基於Chord協議的應用而定,目標節點可能負責儲存關聯到key的一組資料。Chord使用了一致性hash演算法[11](consistent hashing)的變體把Chord網路中的節點關聯到特定而唯一的key上。一致性hash演算法可以解決Server Load Balancer(load balance)問題,因為它能保證系統中的每個節點都能接收到數量相當的key,並且當節點加入、離開系統時,減少資料的遷移。【譯註:實際上一致性hash演算法可以保證:當節點離開時,僅需要遷移離開節點上的資料;當節點加入時,僅將加入節點的後繼節點的部分資料移轉到新節點上;而不涉及到其它的節點和資料,這在理論上已經是最小化的資料移轉了】
【譯註:一致性hash演算法的快速入門可以參見我的另一篇blog——一致性Hash演算法
,淺顯易懂】
在以前對一致性hash演算法的研究中,都假設每個節點都關注系統中的大多數節點,因此難以擴充應用到有很多節點的大規模系統中。相反,在Chord系統中,節點僅需要路由資訊(routing information),關注少數其它節點。因為路由表是分布式的,一個節點執行查詢時,需要和少數的其它節點通訊,穩定點下,在一個有N個節點的系統中,每個節點僅需要維護O(logN)的節點資訊,並且執行查詢時僅需要和其它節點互動O(logN)的資訊。當有節點加入、離開系統時,為了維護路由資訊,Chord保證系統中節點之間的互動訊息不會超過O(logN*logN)個。
Chord具有區別於其它P2P尋找協議的3個特徵:簡單、正確性已經被證明和效能已經被證明。Chord簡單有效,將key傳遞到目的節點僅需要路由O(logN)個節點。為了實現有效路由,每個節點僅需要維護系統中O(logN)個節點的資訊,並且在路由資訊到期時,效能會優雅降級。這在實際中是很重要的,因為節點會隨意的加入、離開系統,很難維持在O(logN) 的事件狀態一致性。Chord協議中,每個節點僅有部分資料正確就能保證查詢路由的正確性(儘管會變慢)。Chord具有簡單的演算法,能在動態環境中維護路由資訊。
文章其餘部分的結構:第二節比較了Chord和其它的相關研究,第三節介紹了促進Chord協議的系統模型。第四節介紹了基本的Chord協議,並且證明了Chord協議的幾個性質,第五節描述了能夠處理多個節點同時加入、離開系統的擴充協議。第六節模擬和在實際部署的原型上的實驗,論證了我們關於Chord效能的聲明。最後,第七節列舉了將來的工作,並在第八節總結了本文的成果。

2 相關工作

Chord將key映射到節點上,而傳統的名字和定位協議提供從key到value的直接映射。value可能是地址、文檔或者任意的資料項目。Chord可以很容易的實現該功能,只需要把每個key/value對儲存到由key映射到的節點上即可。因為這個原因並便於更明確的對比,本節的其它部分假設一個基於Chord、提供從key到value映射的服務。
DNS
[15]提供從主機名稱到IP地址的映射,Chord也能提供相同的服務,以名字作為key,以IP地址作為關聯到key上的value。Chord不需要專門的伺服器,而DNS要依賴一組root網域名稱伺服器機。DNS網域名稱是結構化的資料,以反映管理邊界,Chord則沒有命名結構。DNS是專門為尋找命名主機或服務而設計的,而Chord還能用來尋找沒有綁定到特定主機的資料。
Freenet P2P儲存系統
[4, 5],類似於Chord,具有去中心化、對稱、節點加入和離開時的自適應性等特點。Freenet不會把文檔許可權分配到特定的伺服器上,而是以尋找緩衝的副本的形式來實現查詢。這使Freenet可以提供一定程度的匿名性(anonymity),但是也使Freenet不能保證一定可以擷取(retrieval)到現有的文檔或者保證擷取操作的時間下限。Chord不提供匿名性,但是它的查詢操作能在可預見的時間內完成,並且返回結果只有成功和確定失敗兩種情況。
Ohaha系統
採用了類一致性hash演算法來把文檔映射到節點上,和類Freenet風格的查詢路由[18],因此它也具有Freenet的某些缺點。Archival Intermemory採用了離線計算樹把邏輯地址映射到儲存資料的節點上[3]。
Globe系統
[2]具有廣域定位服務(wide-area location service),把物體標識符(identifier)映射到移動物體的位置上。Globe把網際網路作為一個具有地理、拓撲或者管理域的層次關係組織起來,從而有效構建了一個靜態世界範圍的尋找樹,很像DNS。物體的資訊被儲存在一個特定的葉子域[12]【譯註:leaf domain,翻譯真彆扭】中,指標緩衝(pointer cache)提供了快捷的搜尋路徑。Globe通過使用類hash技術把物體劃分到多個根物理伺服器中,以達到在邏輯伺服器上的高負載處理能力。Chord很好的實現了hash方法,因此可以在不引入任何階層的情況下達到可擴充性,Chord也沒有像Globe一樣利用網路位置資訊。
由Plaxton等人開發的分布式資料定位協議
,可能是與Chord最接近的演算法,該協議的一個變體被應用於OceaStore中。它提供了比Chord更強的保證:和Chord一樣,它保證查詢能在對數級的跳數內完成,並且key的分布是平衡的,但是Plaxon協議還保證:以網路拓撲為準,查詢所經過的網路距離,絕不會遠於儲存key的節點的距離。Chord的優勢在於它更簡單,並能更好的處理節點的並發加入、離開的情況。Chord還和PAST[8]中使用的定位演算法Pastry相似,然而Pastry是一個基於首碼的路由演算法,在其它細節方面也和Chord有所不同。
CAN
用一個d維的笛卡爾座標空間(d是固定的)實現一個把key映射到value的分布式hash表。每一個節點維護O(d)的資訊,查詢複雜度為O(d*N^1/d)。因此,對比Chord,一個CAN網路中的節點維護的資訊不依賴於網路規模N,而且查詢複雜度增長速度比O(logN)高,如果d=logN,那麼CAN的查詢複雜度和資訊維護的空間複雜度都和Chord相同。但是在CAN的設計中,d不能隨著N而動態改變,因此這僅出現在當N恰好和d匹配時。CAN需要一個額外的維護協議以周期性的把key重新對應到節點中,Chord的另一個優勢在於,當存在部分不正確的路由資訊時仍具有健壯的正確性。
Chord的路由過程可以被看作是Grid定位系統[14](Grid location system)的一維類比。Grid依賴於真實世界的地理位置資訊來路由查詢,Chord把節點映射到一個類比的一維空間中,其中的路由被一個和Grid相似的演算法來執行。
Chord可以被用作查詢協議來實現不同的系統,就像第三節中所描述的那樣。特別的,它可以用來預防Napster[17]中的單點故障(single points of failure)或控制,以及像Gnutella[10]那樣廣泛因使用廣播(broadcast)而造成的缺乏擴充性。

3 系統模型

通過處理下面的這些難題,Chord簡化了P2P系統和應用的設計:
負載平衡(Load balance)
:Chord像一個分布式hash函數,最終將key散布在所有的節點上,這就在一定程度上提供了的天然的負載平衡能力。
去中心化(Decentralization)
:Chord是完全分布式的,沒有哪個節點是更重要的,這增強了健壯性,並使得Chord適合於鬆散組織的P2P應用。
可擴充性(Scalability)
:Chord查詢的複雜度隨著節點數目N的log層級而增加(註:即O(logN)),因此適合應用在大規模系統中,並且不需要調整參數來達到這種可擴充性。
可用性(Availability)
:Chord會自動調整內部表來反映新近加入節點和節點失效的情況,確保除了底層網路的嚴重失效外,負責key的節點總是可以查詢到。這甚至在系統處於一個持續改變的狀態時也是正確的。
靈活的命名規則(Flexible naming)
:Chord對於要尋找的key的結構沒有施加任何的限制:Chord的key空間是扁平的(flat)。這就給應用程式與極大的靈活性來決定如何將他們自己的名字映射到Chord的key空間中。
Chord軟體採取library的方式與使用它的用戶端和服務端程式串連。應用程式和Chord主要在兩個方面互動。1 Chord提供一個lookup(key)演算法,返回負責key(儲存key以及與key關聯的資料)的節點的IP地址(註:即節點必要的資訊)。2 每個節點上的Chord通知應用程式該節點所負責的key的變動。這允許應用程式執行一些操作,例如,在一個新節點加入時移動相應的資料到新節點上。
使用Chord的應用程式負責提供任何需要的認證、緩衝、複製,和方便使用的資料命名。Chord的扁平key空間使得這些功能易於實現。比如一個程式可以驗證資料d,這通過將d儲存在一個來源於d的加密hash的Chord key下。類似的,應用程式可以複製資料d,這通過把d儲存在兩個不同的來源於d的應用程式層標識符的Chord key下。
下面就是一些Chord可以提供良好基礎的應用例子:
協同鏡像(Cooperative Mirroring)
,在一個近期的論文中描述[6]。設想有一組軟體開發人員,每個人都希望可以提交發布。發布之間的需求可能差異巨大,從相當流行到不太流行【譯註:這裡看起來應該是影響的範圍,有些發布可能影響很多人,有些發布影響範圍比較小,原文:from very popular just after a new release to relatively unpopular between releases.】。一個有效方法就是讓開發人員之間相互鏡像另一個人的發布。理想情況下,鏡像系統可以提供伺服器之間的Server Load Balancer,複製、快取資料,並且保證真實性。為了可靠性,這樣一個系統應該完全的去中心化,而且也沒有一個天然的中心管理組織。
時間共用儲存(Time-Shared Storage)
,用於斷續串連的節點。如果希望一些資料總是可用的,但是他們的機器只能是偶爾可用,那麼當機器可用時,他們可以相互儲存其它人的資料,反過來,他們的資料也儲存在了另外的機器上【譯註:這樣當他們的機器不可用時,也可以從其它活動的機器上取得資料】。資料的名字可以用作key,在任何時候定位負責儲存的(活動的)Chord節點。這裡有很多問題和協同鏡像應用是相同的,儘管這裡的焦點是可用性而不是負載平衡。
分布式索引(Distributed Indexes)
,支援類似於Genutella或者Napster的關鍵字查詢。本類應用中,key可能來自於要求的keyword,value可能是儲存包含這些keyword的文檔的機器列表。
大規模組合查詢(Large-Scale Combinatorial Search)
,比如密碼破解。這種情況下,key是問題(比如密鑰)的候選解決方案;Chord把這些key映射到負責測試它們執行破解任務的機器上。
圖1顯示了協同鏡像系統的一個可能的3層軟體架構。最高層為使用者提供一個類似檔案系統的介面,包括方便使用的命名和認證機制。這個“檔案系統”層可能實現命名檔案夾和檔案,並將對它們的操作映射到底層的塊操作。下面一層是一個“Block Storage”層,實現需要的塊操作。它負責塊的儲存、緩衝和複製。“Block Storage”層將使用Chord來識別負責儲存一個塊的節點,然後聯絡節點上的Block Storage伺服器來讀寫塊。


圖1 基於Chord的分布式儲存系統結構圖

4 基礎Chord協議

Chord協議明確說明了如何尋找key的位置,新節點如何加入到系統中,以及如何從節點失效中恢複。本節描述了一個沒有處理並發的節點加入、失效的簡化版協議。第五節描述了對該基礎協議的增強來處理節點的並發加入和失效情況。

4.1 概述

Chord的核心就是提供了一個hash函數的快速的分散式運算,該hash函數把key映射到負責key的節點。Chord使用了一致性hash演算法[11, 13],它具有幾個很好的性質。該hash函數能在很大機率上實現平衡負載(所有的節點接收到大概相同數量的key)。並且在很大機率上,當一個節點加入(或離開)網路時,僅有一小部分key會被移動到另外的位置——顯然這是為了維持Server Load Balancer所需的最小移動了。
通過避免讓每個節點知道其它所有節點的資訊,Chord提高了一致性hash演算法的可擴充性。在Chord網路中,每個節點僅需要知道包含少量其它節點的路由資訊(routing information)。因為這個路由資訊是分布式的,一個節點需要通過和少數其它節點通訊來解答hash函數【譯註:完成hash查詢】。在一個具有N個節點的網路中,每個節點只需要維持O(logN)的節點資訊,並且一次查詢只需要O(logN)的互動資訊。
Chord必須在節點加入、離開網路時更新路由資訊,一次加入或離開更新需要O(logN*logN)的互動資訊量。

4.2 一致性hash演算法

一致性hash演算法使用一個hash演算法比如SHA-1[9]為每個節點和key分配一個m-bit的標識符。一個節點的標識符通過節點的IP地址的hash結果得到,而一個key的標識符通過hash這個key而得到。我們將使用術語key來同時指原始的key和hash後的結果,因為在上下文中它的含義是清晰的。相似的,術語“節點”也會同時指節點本身和節點hash後的標識符。標識符的長度必須足夠長,以使得兩個節點或key具有相同標識符的可能性可以忽略不計。
一致性hash演算法採用如下的方式將key分配到節點上。標識符Identifier以Identifier mod 2^m【譯註:mod為模數運算】的順序排列到標識符環上。Key k被指派到標識符環上的第一個標識符等於或者緊隨k(的標識符)的節點上。這個節點就是key k的後繼(successor),記作successor(k)。如果標識符是從0到2^m-1的一圈數字,那麼successor(k)就是在環上從k出發順時針遇到的第一個節點。
圖2是一個m=3的標識符環,上面有0、1、3這3個節點。標識符1的後繼就是節點1,key 1將被定位到節點1上。同樣的,key 2將被定位到節點3上,key 6將被定位到節點0上。


圖2 一個有3個節點的標識符環的例子

 

一致性hash演算法設計的目的就是在節點加入、離開網路時做最小的資料分裂操作。為了維持正確的hash映射,在節點n加入網路時,最初分配給n的後繼的key需要重新分配給n;當節點n離開網路時,n上所有的key將被分配給n的後繼。除此之外不會再有key重新分配的情況。在上面的例子中,如果一個標識符為7的節點加入網路,那麼標識符為6的key將被從節點0重分配到節點7上。
一致性hash演算法的論文[11, 13] 證明了下面的結果:
定理1
對於任意N個節點和K個key的集合,在很大機率上(with high probability):
1 任何一個節點最多負責(1+cta)K/N個key;
2 當第N+1個節點加入或離開網路時,O(K/N)個key需要重新分配(並且僅是分配給加入的節點或從離開的節點上分配出去);
當一致性hash演算法按照上面的描述實現時,定理可以保證cta=O(logN)。論文還證明了通過把每個節點以O(logN)個“虛擬節點”的方式運行,cta可以被降低到一個任意小值。
術語“很大機率上”需要一些討論。一個簡單的解釋就是節點和key都是隨機播放的,這在一個非對抗性的世界裡貌似是可信的【譯註:non-adversarial,看下面兩句可理解】。機率分布是建立在隨機播放的key和節點上的,因此這樣的一個隨機播放是不可能產生一個不平衡的分布的。然而,有人可能會擔心,一個對手估計選擇映射到相同標識符的key,來破壞其Server Load Balancer的特性。一致性hash論文使用了“k-universal hash functions”來保證出現非隨機播放的key的情況。
我們選擇使用標準的SHA-1演算法作為基本的hash演算法,而不是使用“k-universal hash function”。這使得我們的協議是確定性,因此“很大機率上”的聲明也不再具有意義。但是使用SHA-1產生一組有衝突的key,或者破解SHA-1演算法,被認為是極其困難的。因此,我們稱協議具有“基於標準的強度假設”的性質(based on standard hardness assumptions),而不是“很大機率上”。
為了簡單性,我們沒有使用虛擬節點。在這種情況下,很大機率上(或者基於標準的強度假設)一個節點的負載可能會超出平均值一個係數。避免虛擬節點的一個原因是所需的虛擬節點個數由系統中的節點個數決定,而難以選擇。當然,你可以在系統中選擇使用一個預定義的虛擬節點上限,比如我們可以要求一個IPV4地址最多隻能運行一個Chord服務,這樣一個物理節點作為32個虛擬節點運行將會提供較好的Server Load Balancer性。

4.3 可擴充的key定位

少量的路由資訊有助於在一個分布式的環境中實現一致性hash演算法。每個節點僅需關注它在環上的後繼節點。一個給定標識符identifier的查詢可以在環上沿著後繼進行,直到第一次遇到identifier的後繼,它就是要查詢的節點。Chord協議維護了這些後繼指標,以保證能正確的解決每次查詢。然而,這種解決方案是低效的:它可能需要遍曆所有的節點來找到合適的映射。為了加速尋找,Chord還維護了額外的路由資訊,這些額外的資訊並非為了正確性,當然只要後繼的資訊是正確的,它們的正確性就能得到保證。
和前面一樣,設key和節點的標識符都是m-bit的。每個節點維護一個有m項(最多)的路由表,又稱為finger table。節點n的第i個表項儲存了節點s,並且s是在標識符環上距離n至少為2^(i-1)的第一個節點,即s=successor(n+2^(i-1),其中1<= i <=m(所有的計算都基於模2^m)。我們稱s為n的第i個finger,並標記為n.finger_t[i].node(見表1)【譯註:為了不至於混淆finger和finger table,本文將原文中代表finger table的變數finger都改為finger_t】。finger table中的項包括相應節點的IP和連接埠資訊。注意到節點n的第一個finger就是n在環上的後繼,方便起見,我們經常稱它為後繼(successor)而不是第一個finger。

表1-1 節點n的變數定義(finger table在下表列出)

 

【譯註:為避免混淆,本文將原文中的一個表分成了兩個表項,並加上了說明列,做簡單的必要補充說明】
3(b)中的例子所示,節點1的finger table將指向 (1 + 2^0) mod 2^3 = 2,(1 + 2^1) mod 2^3 = 3和(1 + 2^3) mod 2^3 = 5等3個標識符的後繼。分別的,標識符2的後繼是3,因為3節點緊跟標識符2的第一個節點,標識符3的後繼是3,而標識符5的後繼是0。

表1-2 節點的finger table表定義,對應於m-bit的標識符

 

【譯註:根據定義,Finger table中的第一個finger,即第一個大於等於finger_t[1].start的節點,就是節點n的後繼,n.successor = n.finger_t[1].node】
該模式有兩個重要的性質:第一,每個節點只需儲存少量的其它節點資訊,並且在標識符環上,它知道的距離較近的節點比較遠的節點更多;第二,一個節點的finger table通常並不包括足夠的資訊來確定任意key的後繼。比3中的節點3並不知道標識符1的後繼,因為1的後繼(節點1)並不在節點3的finger table中。
如果一個節點n不知道key k的後繼,它會怎麼做呢?如果n能夠找到一個標識符比自己更接近k的節點t,t將會比n知道更多的k地區的資訊【譯註:上面的性質1】。因此n尋找它的finger table找一個標識符在k前面並且最接近k的節點j,並問j它知道誰的標識符更接近k。通過重複這個過程,n就會知道越來越接近k的節點。


圖3 (a)節點1的finger區間 (b)節點0,1,3和key1,2,6,finger table和key位置

 

搜尋的虛擬碼4所示【譯註:代碼中添加了響應的注釋】。符號n.foo()表示函數foo()在節點n上執行。遠程調用和變數引用前有遠程節點標識,本地的調用和變數引用略去了本地節點標識。因此n.foo()代表了一個在節點n上的遠程調用,而不帶“()”的n.bar,則表示在節點n上尋找變數bar的遠程調用。
函數find_successor尋找給定indentifier的直接前驅n,於是n的後繼肯定也是identifier的後繼。我們將實現函數find_predecessor,因為在後面的jion操作中也會用到(4.4小節)。
當節點n執行find_predecessor(id)時,它沿著Chord環上的一系列節點接近id。如果n聯絡到了節點n’,並且id落在了n’和n’的後繼之間,find_predecessor將結束並返回n’。否則n詢問n’知道的最接近id並在id之前的節點。因此演算法將一直向著id的前驅執行。
比如,考慮圖3(b)中的Chord環。假設節點3要查詢標識符1的後繼。因為1屬於環形區間[7, 3),即3.finger_t[3].interval,因此節點3檢查其finger table的第三項,就是0。因為0在1之前,節點3將向節點0尋找1的後繼。作為應答,節點0從推斷出1的後繼就是節點1本身,因此返回1給3。

// 委託節點n尋找id的後繼<br />n.find_successor(id)<br /> n’ = find_predecessor(id); // n的本地調用<br /> return n’.successor; // RPC查詢得到的n’的後繼<br />// 委託節點n尋找id的前驅<br />n.find_predecessor(id)<br /> n’ = n;<br /> // 這裡尋找的是前驅,id不能在n’中,因此區間是前開後閉<br /> while(id NOT IN (n’, n’.successor]) // n’.successor由RPC查詢得到<br /> n' = n’. closed_preceding_finger(id); // n’上的RPC<br /> return n’;<br />// 返回節點n的最接近id且在id之前的finger,都是本地調用<br />n.closed_preceding_finger(id)<br /> for i=m down to 1 // 從最大區間開始,每次迭代1/2遞減<br /> if(finger_t[i].node IN (n, id)) then // 落在區間內則找到<br /> return finger_t[i].node;<br /> return n;

圖4 尋找虛擬碼

finger指標的倍增前進使(節點)和目標標識符的距離在find_predecessor中的每次迭代中減半。從這種直覺我們可以匯出下面的定理:
定理2
在很大機率上(或者基於標準的強度假設),在一個N個節點的網路中,尋找一個後繼所需要聯絡的節點個數為O(logN)。
證明:假設節點n希望查詢k的後繼,且p是k的直接前驅結點,我們將分析到達p的步數。
回憶如果n != p,那麼n將把查詢轉交給在finger table中最接近k的前驅。假設p在n的第i個finger區間中,因此該區間是非空的,n將在該區間找到節點f。節點n和f之間的(標識符)距離至少是2^(i-1)。但是f和p都在n的第i個finger區間中,因此n和f之間的距離最大是2^(i-1)。這意味著f距p比n近,或者說,從f到p的距離最多是從n到p的距離的二分之一。
如果在每次迭代時,接手查詢的節點和p的前驅的距離以1/2遞減,並且最初距離有2^m,那麼在m步內距離將會減到1,意味著我們已經到達p。
實際上,像上面所討論的,我們假設節點和key的標識符都是隨機的,因此在很大機率上,查詢的轉交次數是O(logN),經過O(logN)轉交之後,當前處理節點和key之間的距離將會降到2^m/N,我們期望落在該範圍內的節點個數是1,很大機率上可能是O(logN)。
【譯註:如果節點分布完全均衡,則2^m/N的範圍僅含有一個節點,根據前面一致性hash的定理可知,有一個不均衡因子cta,在Chord的hash演算法中,cta的取值很可能就是O(logN),在機率意義上節點間的距離最小可能是2^m/(N*O(logN)+1),因此在2^m/N的範圍內可能會含有O(logN)個節點】
因此即使在剩下的步驟中每次只前進一個節點,遍曆整個區間並達到key所需要的步驟數也在O(logN)之內。證畢
在第六節的實驗結果報告中,我們將看到查詢的平均時間是1/2logN。

4.4 節點加入

在動態網路中,節點可以在任何時候加入(離開)網路。實現這些操作的主要挑戰就在於要保持網路根據key定位元據的能力,為了保持這種能力,Chord需要保證兩個不變性:
1 每個node的後繼都被正確的維護;
2 對於任意的key k,節點successor(k)是負責k的節點;
為了快速的查詢,Chord也要求finger table是正確的。【譯註:從後面可以看到,finger table並不總是正確的】
本節描述了如何在單個節點加入網路時如何維護這些不變性,我們將在第五節描述多個節點同時加入網路的情況,同時還描述了節點失效時的處理。在描述節點加入操作前,我們先總結它的效能(定理的證明參見合作技術報告[21]):
定理3
在很大機率上,節點加入、離開N各節點的網路時將需要O(logN*logN)的訊息來重建Chord的路由不變性和finger table。
為了簡化加入、離開機制,Chord中的每個節點都維護著一個predecessor指標,它包含節點直接前趨的Chord標識符和IP地址,因此可以逆時針的遍曆標識符環。
為了保證上面聲明的不變性,當節點n加入時Chord必需執行下面的3個任務:
1 初始化n的前驅結點指標predecessor和finger table
2 更新已存在節點的finger table和predecessor指標,以反映n的加入
3 通知上層軟體,讓它知道n的加入,以把n負責的狀態(比如values)轉移到n上
我們假設新節點通過一些外部機制可以擷取一個Chord中的節點n’的標識符。節點n通過n’來初始化自己的狀態資訊,並且加入到Chord網路中,如下面所示的那樣。
初始化predecessor和finger table:節點n請求n’在網路中查詢它的predecessor和finger table,已完成初始化,使用的是init_finger_table,圖6是它的虛擬碼。為finger table的m個表項依次執行find_successor來完成初始化,需要的時間是m*O(logN)。為了減少時間,對每個i,n都檢查第i個finger是否和和第i+1個finger是相同的。這在finger_t[i].interval不包含任何節點時是成立的,因此finger_t[i].node >= finger_t[i+1].start。可以證明這個改進在很大機率上可以使需要執行查詢的finger數目減少到O(logN),從而將總時間減少到O(logN*logN)。
一個實踐上的最佳化,新加入的節點n可以提取複寫一個鄰居的完整finger table和predecessor指標。n可以使用這些表的內容給自己的finger table設定正確的值,因為n的表和它鄰居的是相似的,可以證明這可以把設定finger table的時間減少到O(logN)。


圖5(a) 節點6加入後,finger table和key位置的變化情況,變化部分用黑色標記
譯註:為了便於對比,譯文一併給出了前後兩幅圖

 
圖5(b) 節點1離開後,finger table和key位置的變化情況,變化部分用黑色標記
譯註:為了便於對比,譯文一併給出了前後兩幅圖

 

#define successor finger_t[1].node<br />// 節點n加入網路,n’是網路中的任意節點<br />n.join(n’)<br />if(n’)<br /> init_finger_table(n’);<br /> update_others();<br /> 把在(predecessor, n]中的key從successor中轉移過來<br />else // n是網路中的唯一節點<br /> for i=1 to m<br /> finger_t[i].node = n;<br /> predecessor = n;<br />// 初始化本地節點n的finger table<br />// n’是網路中的任意節點<br />n.init_finger_table(n’)<br /> finger_t[1].node=n’.find_successor(finger_t[1].start);<br /> predecessor = successor.predecessor;<br /> successor.predecessor = n;<br /> for i=1 to m-1<br /> if(finger_t[i+1].start IN [n, finger_t[i].node))<br /> finger_t[i+1].node = finger_t[i].node;<br /> else<br /> finger_t[i+1].node =<br /> n’.find_successor(finger_t[i+1].start);<br />// 更新那些finger table應該指向n的節點<br />n.update_others()<br /> for i = 1 to m<br /> // 尋找第i個finger是n的節點p<br /> p = find_precessor(n – 2^(i-1));<br /> p.update_finger_table(n, i);<br />// 如果s是n的第i個finger,更新其finger table<br />n.update_finger_table(s, i)<br /> if(s IN [n, finger_t[i].node))<br /> finger_t[i].node = s;<br /> // 擷取n的直接前驅p<br /> p = predecessor;<br /> // 遞迴更新<br /> // 可能需要將p的第i個finger更新為s<br /> // 因為s也可能在[p, p.finger_t[i].node)區間<br /> p.update_finger_table(s, i);

圖6 節點加入操作的虛擬碼

更新已存在節點的finger:節點n需要加入到一些已存在節點的finger table中,比如在圖5(a)中,節點6成為節點0和1的第三個finger,成為節點3的第一和第二個finger。
圖6描述了更新已有finger table的update_finger_table函數的虛擬碼。節點n將成為節點p的第i個finger,若且唯若:(1)p在n前至少2^(i-1)的距離,(2)p的第i個finger在n的後面。
【譯註:n=p.finger_t[i].node >= finger_t[i].start = (p+2^(i-1)) mod 2^m => n >= (p+2^(i-1)) mod 2^m;因為n更靠前,於是需要更新p的第i個finger】
能夠滿足上面兩個條件的節點p是n-2^(i-1) mod 2^m的直接前驅。因此給定n,演算法從n的第i個finger開始,然後逆時針方向遍曆環,直到遇到一個第i個finger在n前面的節點。
我們在技術報告[21]中證明了,在很大機率上,當節點加入到網路時需要更新的節點個數是O(logN)。尋找和更新這些節點需要的時間是O(logN*logN)。一個更複雜的機制可以把時間減少到O(logN);然而,我們不準備描述它,因為我們將在後面的章節中使用該演算法。
Key的轉移:n加入網路時最後需要執行的一個操作就是把所有後繼為n的key轉移到n上。毫無疑問,細節取決於基於Chord的上層應用程式,但是典型的操作將會涉及到把這些key關聯的資料轉移到新節點n上。節點n僅僅會成為原先被緊接著n的節點【譯註:n的後繼】負責的部分key的後繼,因此n只需要聯絡它並把相應的key轉移過來就行了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.