計算機網絡--詳解P2P對等網絡(二)—Chord算法研究與分析

結構化與非結構化網絡

非結構化的P2P網絡是指網絡節點之間不存在組織關係,節點之間完全是對等的,比如第一代P2P網絡Napster。

結構化的P2P網絡與非結構化恰好相反,我們認爲網絡在邏輯上存在一個人爲設計的結構,比如Chord假定網絡是一個環,Kadelima則假定爲一顆二叉樹。有了這些邏輯結構,就給我們資源查找引入了更多的算法和思路。


引言

我們在 計算機網絡–詳解P2P對等網絡(一)—BitTorrent協議 這一篇博客中講述了BT下載的過程:在對等用戶拿到種子文件的時候,首先會聯繫tracker服務器,然後加入用戶集羣,並在用戶集羣中尋找自己所需的內容,最後與擁有內容的對等用戶進行聯繫。

從BT下載的過程中引出本節所要討論的問題:如何高效的從用戶集羣中找出哪些對等用戶擁有你正在尋求的具體內容?

在歷史中有三種比較典型的模型來解決這個問題:

  • Napster:使用一箇中心服務器接收所有的查詢,服務器告知去哪下載其所需要的數據。存在的問題是中心服務器單點失效導致整個網絡癱瘓。

  • Gnutella:使用消息洪泛(message flooding)來定位數據。一個消息被髮到用戶集羣內每一個節點,直到找到其需要的數據爲止。存在的問題是消息數與節點數成線性關係,導致網絡負載較重。

  • SN型:超級節點(Super Node),SN保存網絡中節點的索引信息,這一點和中心服務器類型一樣,但是網內有多個SN,其索引信息會在這些SN中進行傳播,所以整個系統的崩潰機率就會小很多。儘管如此,網絡還是有崩潰的可能。

關於P2P網絡拓撲結構更詳細的內容,請參考:P2P網絡的拓撲結構

現在的研究結果中,Chord、Pastry、CAN和Tapestry等常用於構建結構化P2P的分佈式哈希表系統。

Chord算法是麻省理工學院(MIT)提出的一種基於DHT技術的結構化P2P路由協議,具有完全分佈式、負載均衡、可用性及可擴展性好、命名方式靈活等特點。本文主要對Chord算法展開分析。


分佈式哈希表(DHT)

對於本節問題的思考,我們可以給出一種基本的解決方案:每個對等節點維護了一張路由表(索引),這張路由表只保存了少量有關其他節點的信息,這個特點意味着它保持最新索引的代價不會很昂貴。其次,每個節點可以快速的查看索引中的表項,否則,它就不是個有效的索引。最後,每個節點可以同時使用索引,即使其他節點來來去去,這個屬性意味着索引的性能隨着節點數量的增長反而越來越好~

該解決方案就被稱爲分佈式哈希表,因爲對等節點所維護的路由表就是一張索引表,而索引的基本功能就是將一個關鍵字映射到一個值。這簡直就是一張哈希表,但是我們的解決方案是分佈式版本。我們可以再看一下維基對於DHT的定義:

分佈式哈希表(distributed hash table,縮寫DHT):分佈式計算系統中的一類,用來將一個關鍵值(key)的集合分散到所有在分佈式系統中的節點,並且可以有效地將消息轉送到唯一一個擁有查詢者提供的關鍵值的節點(Peers)。這裏的節點類似散列表中的存儲位置。分佈式散列表通常是爲了擁有極大節點數量的系統,而且在系統的節點常常會加入或離開(例如網絡斷線)而設計的。在一個結構性的覆蓋網絡(overlay network)中,參加的節點需要與系統中一小部分的節點溝通,這也需要使用分佈式散列表。

上述我特意加粗的語句,正是對P2P網絡架構的描述。

如果對於DHT的概念還抱有一定的疑惑,可以在網上搜尋更白話的說明,博主不再進行貼出。

DHT與一致性哈希

如上所述,DHT的主要想法是把網絡上資源的存取像哈希表一樣,可以簡單而快速地進行put、get。與一致性哈希相比,DHT更強調的是資源的存取,而不管添加刪除節點時產生的資源震盪的問題。與一致性哈希相同的是,DHT也只是一個概念,具體細節留給各實現。

當前這些P2P實現可以被作爲DHT的具體實現,再次列舉一些有代表性的實現:

Chord、CAN、Tapestry、Pastry、Apache Cassandra、Kadelima、P-Grid、BitTorrent DHT


Chord算法

Chord是什麼?

Chord是一個算法,也是一個協議。作爲一個算法,Chord可以從數學的角度嚴格證明其正確性和收斂性;作爲一個協議,Chord詳細定義了每個環節的消息類型。當然,Chord之所以受追捧,還有一個主要原因就是Chord足夠簡單,3000行的代碼就足以實現一個完整的Chord。

Chord概述

Chord的實現方式如下:給定一個關鍵字Key,將其映射到某個節點。爲此,採用相同哈希函數(SHA-1)爲每個節點和關鍵字產生一個m bit的ID,並按照ID大小構成環形拓撲。節點所產生的ID被稱爲節點標識符,關鍵字所產生的ID我們稱它爲關鍵字ID。運行Chord的主機相互連接構成Chord網絡,這是一個建立在IP網絡之上的覆蓋(overlay)網絡。每個節點N有2個鄰居:以順時針爲正方向排列在N之前的第1個節點稱爲N的前繼(predecessor),在N之後的第1個節點稱爲N的後繼(successor)。如下圖(藍色節點爲節點ID,白色節點爲關鍵字ID):

此處輸入圖片的描述

同一致性哈希一樣,資源放置在關鍵字ID的後繼節點上,如上圖,資源2被放置在節點3中。

Finger表

我們在本篇博客 分佈式哈希表(DHT) 一節中已經講過,每個對等節點都會維護一張路由表,以便在用戶集羣中尋找擁有所需資源的其他對等節點。這張路由表就被稱爲Finger表,Finger表的表項大小爲m,由兩列數據項組成,如下:

ID+2的i次方 successor

其中ID就代表節點標識符,i表示Finger表中表項的下標,從0開始,successor則表示存儲資源的後繼節點。

舉個例子:我們現在有一個m = 3的Chord環,它可以容納2的3次方,也就是8個節點。現在有4臺機器,假設它們經過哈希之後所產生的ID爲0,1,2,6,那麼機器1中將要維護的Finger表如下:

i ID+2的i次方 successor
0 2 2
1 3 6
2 5 6

其中ID+2的i次方表示的是關鍵字ID。

對於上表的解釋,由一致性哈希可知:

機器1本地存儲着關鍵字ID爲1的數據,機器2本地存儲着關鍵字ID爲2的數據,機器6本地存儲着關鍵字ID爲3,4,5,6的數據,機器0本地存儲着關鍵字ID爲7,0 的數據。

與此同時,如上表,機器1上,還存儲着關鍵字ID爲2,3,5的數據所在的機器地址。比如,機器1知道,關鍵字ID爲5的數據存儲在機器6上面。

Chord的查找

Chord採取冪次逼近查詢法。任何一個節點收到查詢關鍵字ID爲“K”的請求時,首先檢查K是否落在該節點標識和它的後繼節點標識之間,如果是的話,這個後繼節點就是存儲目標(K, V)對的節點。否則,節點將查找它的Finger表,找到表中節點標識符最大但不超過K的節點,並將這個查詢請求轉發給該節點。通過重複這個過程,最終可以定位到K的後繼節點,即存儲有目標(K, V)對的節點。

比如,當機器1接收到查詢關鍵字ID爲7的數據在哪臺機器上時,它發現關鍵字ID“7”並不在該節點標識符和它的後繼節點標識符之間,因此它查找節點標識符最大但沒有超過7的節點,爲6,於是將查詢請求轉發到機器6上。

機器6的路由表按照上述規則進行生成,如下(環形拓撲):

i ID+2的i次方 successor
0 7 0
1 0 0
2 2 2

機器6上的路由表指出:關鍵字ID爲7的數據在機器0上… …重複這個過程,最終可找到保存關鍵字ID爲7的資源的節點。

通過在每臺機器上保存m項的路由信息,上面的方式可以做到O(logN)的查詢時間複雜度。另外,比如Amazon Dynamo在論文中所說:通過在每臺機子上保存足夠多的路由信息,理論上可以做到O(1)時間的查詢(相應的,節點間冗餘信息也會更多)。

節點的加入

新節點的加入需要一個稱爲嚮導的已知節點(n0)進行協助,任何一個運行在Chord網絡中的節點都可以充當這個角色。加入過程包括新節點本身的Join操作和被其他節點發現2個階段。如下圖所示,假設np和ns是Chord網絡中相鄰兩節點,n爲新節點,它加入網絡後應該位於np和ns節點之間。

這裏寫圖片描述

新節點的加入有三個操作:

  • Join() :n加入一個Chord環,已知其中有一個嚮導節點n0;
  • Stabilize(): 每個節點在後臺週期性的進行此項操作,查詢自身節點的後繼節點的前序節點是否是自身,如果不是自身,說明有新加入的節點,此時將自身的後繼節點修改爲新加入的節點;
  • Notify(n): n通知其他節點它的存在,若此時其他節點沒有前序節點或n比其現有的前序節點更加靠近自身,則將n設置爲前序節點。

在瞭解了上述三個操作之後,我們討論一下n節點加入的具體過程:

  1. n請求嚮導爲它查找後繼 (即ns),並初始化自身Finger表,按照Finger表的定義,此時只有n對自身屬性進行了設置,其他節點並不知道新節點的加入(如上圖a);
  2. 在n節點將自身的後繼節點修改爲ns後,會對ns進行Notify(n)操作,即n節點通知ns它的存在,此時ns標記n成爲自己的前序節點;
  3. 所有節點會在後臺週期性的進行Stabilize操作,此時np發現ns的前序結點已不是自身,則np將自己的後繼節點修改爲n;
  4. np對n進行Notify(np)操作,n接到通知,將np修改爲自己的前序節點。

在這裏有一個問題:嚮導節點如何幫助新加入的節點尋找它的後繼節點以及新加入的節點如何初始化其Finger表?

第一點:對於新節點n,通過向嚮導節點提交查找n自身節點標識符的請求,嚮導節點檢索其後繼;
第二點:新節點通過向嚮導節點請求ID + 2的m次方從而構建Finger表。

節點的失效

節點的失效是節點沒有通知其他節點而突然離開網絡,這通常由主機崩潰或IP網絡斷開等意外原因造成,此時失效節點的前繼保存的後繼信息變得不可用,從而造成Chord環的斷裂。爲了處理這個問題,需要週期性的對節點的前序和後繼進行探測。如果節點n發現其後繼或前序已經失效,則從Finger表中順序查找第1個可用節點進行替換,並重建Finger表。對前序節點失效的處理仍需要藉助於Notify消息。考慮上圖中的例子,ns雖然能夠感知n的失效卻無法進行修復。由於上述對後繼失效的處理過程能夠保證Chord環後繼鏈的正確性,因此np通過在Stabilize中向新後繼ns發送Notify,把ns的前繼改成np。值得注意的是,其他節點也可能在Finger表項中保存有失效節點的記錄,因此需要多次Stabilize,把失效信息擴散到Chord網絡中。雖然這種方法最終能夠保證Chord網絡的完整性,但在節點頻繁進出的情況下,其效率仍須更深入地研究。

節點的退出

由於節點失效處理方法是穩定的,因此節點的退出可看作爲失效而不採取其他附加措施。但基於效率的考慮,節點n退出時進行如下操作:1. 把n後繼節點的前繼改成n的前繼;2. 把n前繼節點的後繼改成n的後繼;3. 從n前繼的Finger表中刪除n。


總結

  1. 熟悉DHT、一致性哈希、Chord算法之間的概念及聯繫;
  2. 熟悉Chord算法的思想(Finger表的構建、Chord查詢、節點的加入等);
  3. 瞭解P2P網絡的一些其它拓撲結構。

PS:關於Chord算法數學角度上的證明與分析,有興趣的同學可以自行查閱相關資料~


參考閱讀

計算機網絡(第五版) — Andrew S. TanenBaum/David J. Wetherall

結構化P2P網絡chord算法研究與分析

分佈式哈希算法

【學術之門之P2P算法研讀】P2P中的Chord算法

Chord算法(原理)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章