Petar Maymounkov and David Mazi`eres
fpetar,dmg@cs.nyu.edu
?
摘要
本文我們將描述一個在容易出錯的網絡環境中擁有可證實的穩定性和高性能穩定性的點對點(P2P)系統。我們的系統使用一個很新穎的基于異或運算的拓撲來發送查詢并且定位節點, 這簡化了算法并且使驗證更加容易。 這種拓撲結構具有以下特性,它能通過交換消息傳達和加強節點間的有用聯系信息。本系統利用這個信息來發送平行的,異步的查詢消息來對付節點的失效而不會給用戶帶來超時時延。
?
1
.介紹
???
本論文描述Kademlia , 一個點對點(P2P)的<鍵, 值>元組存儲和查詢系統。 Kademlia擁有許多的可喜的特點,這些特點是任何以前的P2P系統所無法同時提供的。它減少了節點必須發送的用來相互認識的配置消息的數量。在做鍵查詢的同時, 配置消息將會被自動傳播。 節點擁有足夠的知識和靈活性來通過低時延路徑發送查詢請求。 Kademlia使用平行的,異步的查詢請求來避免節點失效所帶來的超時時延。通過節點記錄相互的存在的算法可以抵抗某些基本的拒絕服務(DoS)攻擊。 最后, 僅僅使用在分布式運行時間上較弱的假設(通過對現有點對點系統的測量而確認的這些假設),我們可以正式的證實Kademlia的許多重要特性。
??? Kademlia
使用了許多點對點(P2P)系統的基本方法。 鍵是一個160-bit的隱式數量(例如, 對一些大型數據進行SHA-1哈希的值)。 每個參與的機器都擁有一個節點ID, 160位的鍵。 <鍵, 值>對將存儲在那些ID與鍵很‘接近’的節點上, 這里‘接近’當然是按照一個接近度的概念來計算的。最后, 一個基于節點ID的路由算法使得任何人可以在一個目的鍵附近定位到一個服務器。
Kademlia
的許多的優點都是得益于它使用了一個很新穎的方法, 那就是用節點間的鍵作異或運算的結果來作為節點間的距離。異或運算是對稱的, 允許Kademlia的參與者接收來自相同分布的并且包含在其路由表中的節點的查找請求。如果沒有這個性質,就像Chord一樣,系統無法從它們收到的查詢請求中學習到有用的路由信息。更糟的是, 由于Chord中的運算是不對稱的, Chord的路由表更加嚴格。 Chord節點的查找表的每一項都必須存儲精確的按ID域的間隔遞增的節點。在這個間隔內的任何節點都比這個間隔內的某些鍵大,因此離鍵很遠。相反,Kademlia 可以在一定的間隔內發送請求給任何節點, 允許基于時延來選擇路由,甚至發送平行的,異步的查詢。
為了在特定的ID附近定位節點,Kademlia自始至終使用一個單程的路由算法。相反,其它一些系統使用一種算法來接近目標ID,然后在最后的幾個跳數使用另外一種算法。在現有系統中,Kademlia與pastry的第一階段最像,(雖然作者并沒有用這種方式來描述),Kademlia 的異或運算可以使當前節點到目標ID的距離粗略的持續減半,以此來尋找節點。在第二階段,Pastry不再使用距離運算,而是改為比較ID的數字區別。它使用第二種,數字區別運算作為替代。不幸的是,按第二種運算計算的接近比第一種的遠得多,這造成特定節點ID值的中斷,降低了性能,并且導致在最差行為下的正式分析的嘗試失敗。
?
2
.系統描述
每個Kademlia節點有一個160位的節點ID。在Chord系統中,ID是通過某種規則構造出來的,但在這片文章中,為了簡化,我們假設每臺機器在加入系統時將選擇一個隨機的160位值。每條節點發送的消息包含它的節點ID, 同時允許接收者記錄下發送者的存在信息,如果有必要的話。
鍵,同樣也是160位的標識符。為了發布和尋找<鍵,值>對,Kademlia依賴一個概念,那就是兩標識符之間的距離的概念。給定兩個標識符, x和y, Kademlia定義兩者的位異或(XOR)的結果作為兩者的距離,d(x,y)=x⊕y。我們首先注意到異或運算是一個有意義的運算,雖然不是歐幾里得運算。很明顯具有下面的性質: d(x,x)=0;如果x≠y, 則d(x, y)>0;任意的x, y: d(x, y) = d(y, x)。 異或運算還滿足三角性質:d(x, y) + d(y, z) ≥ d(x, z)。 這個三角性質之所以成立是基于下面這個事實: d(x, z) = d(x, y) + d(y, z); 并且任意的a>=0, b≥0: a+b≥a⊕b。
跟Chord的順時針循環運算一樣,異或運算也是單向的。對于給定的一個點x以及距離Δ,僅有一個點y,使得d(x, y) = Δ。 單向性確保所有對于相同的鍵的查詢將匯聚到相同路徑中來,而不管是什么起源節點。因此,在查找路徑上緩存<鍵,值>對可以減少‘撞車’的機會。跟Pastry而不是Chord一樣, 異或運算也是對稱的。(對所有的x以及y, d(x,y) = d(y,x))
?
2
.1.節點狀態
Kademlia
節點存儲互相的聯系信息,以用于路由查詢消息。對于任何0 =< i < 160, 每個節點保存那些到本節點的距離為2i 到2i+1之間的節點信息列表,包括<IP地址,UDP端口, 節點ID>。我們把這些列表稱為K-桶。每個K-桶中的節點按最后聯系的時間排序――最久未聯系的節點放在頭部,最近聯系的節點放在尾部。對于比較小的i值,K-桶通常是空的(因為沒有合適的節點存在于系統中)。對于比較大的i值,列表節點數可以達到k的大小,k是一個系統級別的冗余參數。k值的選擇必須滿足一個條件,那就是任意k個節點在一個小時內都失效的可能性很小(例如k =20)。
圖1:
以當前已在線時間的函數的形式顯示了節點在接下來的一小時后繼續在線的比例。X軸代表分鐘,y軸代表那些已經在線了x分鐘的節點中將繼續在線1小時的比例。
?
當一個Kademlia節點收到來自另外一個節點的任何消息(請求的或者回復的),它將更新自己的一個K-桶,即發送節點ID對應的那個桶。如果發送節點已經存在于接收者的K-桶中,接收者會把它移到列表的尾部。如果這個節點還沒有存在于對應的K-桶中并且這個桶少于k個節點,則接收者把發送者插入到列表的尾部。如果對應的K-桶已經滿了,則發送者將向該K-桶中的最久未聯系節點發送ping命令測試是否存在,如果最久未聯系節點沒有回復,則把它從列表中移除,并把新的發送者插入到列表尾部。如果它回復了,則新的發送者信息會丟棄掉。
K-
桶非常高效的實現了剔除最久未聯系節點的策略,存活的節點將永遠不會從列表中移除。這種偏向保留舊節點的做法是我們對由Saroiu等人收集的Gnutella協議的跟蹤數據進行分析而得出來的。圖1以當前已存在時間的函數的形式顯示了Gnutella節點在一小時后繼續在線的比例。一個節點存活的時間越長,則這個節點繼續存活一小時的可能性越大。通過保留存活時間最長的那些節點,K-桶中存儲的節點繼續在線的概率大大提高了。
K-
桶的第二個優點是它提供了對一定的拒絕服務(DoS)的攻擊的抵抗。系統中不斷涌入新節點并不會造成節點路由狀態的更新過快。Kademlia節點只有在舊節點離開系統時才會向k-桶中插入新節點。
?
2
.2.Kademlia協議
Kademlia
協議由4個遠程過程調用(RPC)組成:PING,STORE,FIND_NODE, FIND_VALUE。 PING RPC 測試節點是否存在。STORE指示一個節點存儲一個<鍵,值>對以用于以后的檢索。
FIND_NODE
把160位ID作為變量,RPC的接收者將返回k個它所知道的最接近目標ID的<IP地址,UDP端口,節點ID>元組。這些元組可以來自于一個K-桶,也可以來自于多個K-桶(當最接近的K-桶沒有滿時)。在任何情況下, RPC接收者都必須返回k項(除非這個節點的所有的K-桶的元組加起來都少于k個,這種情況下RPC接收者返回所有它知道的節點)
FIND_VALUE
和FIND_NODE行為相似――返回<IP地址,UDP端口,節點ID>元組。僅有一點是不同的,如果RPC接收者已經收到了這個鍵的STORE RPC,則只需要返回這個已存儲的值。
在所有RPC中,接收者都必須回應一個160位的隨機RPC ID,這可以防止地址偽造。PING中則可以為RPC接收者在RPC回復中捎回以對發送者的網絡地址獲得額外的保證。
Kademlia
參與者必須做的最重要的工作是為一個給定的節點ID定位k個最接近節點。我們稱這個過程為節點查詢。Kademlia使用一種遞歸算法來做節點查詢。查詢的發起者從最接近的非空的K-桶中取出а個節點(或者,如果這個桶沒有а項,則只取出它所知道的最接近的幾個節點)。發起者然后向選定的а個節點發送平行的,異步的FIND_NODE RPC。а是一個系統級別的并行參數,比如為3。
在這個遞歸的步驟中,發起者重新發送FIND_NODE給那些從上次RPC中學習到的節點(這個遞歸可以在之前的所有的а個RPC返回之前開始)。在這返回的與目標最接近的k個節點中,發起者將選擇а個還沒有被詢問過的節點并且重新發送FIND_NODE RPC給它們。沒有立即作出響應的節點將不再予以考慮除非并且直到它們作出響應。如果經過一輪的FIND_NODE都沒有返回一個比已知最接近的節點更接近的節點,則發起者將重新向所有k個未曾詢問的最接近節點發送FIND_NODE。直到發起者已經詢問了k個最接近節點并且得到了響應,這個查詢才結束。當а=1時,查詢算法在消息開支和檢測失效節點時的時延上與Chord非常相似。 然而,Kademlia可以做到低時延路由因為它有足夠的靈活性來選擇k個節點中的一個去做查詢。
按照上面的查詢過程,大多數的操作都可以實現。要存儲一個<鍵,值>對,參與者定位k個與鍵最接近的節點然后向這些節點發送STORE RPC。另外,每個節點每個小時都會重新發布它所有的<鍵,值>對。這可以以高概率的把握確保<鍵,值>對的持續存在于系統中(我們將會在驗證概略一節中看到)。通常來說,我們還要求<鍵,值>對的原始發布者每隔24小時重新發布一次。否則,所有的<鍵,值>對在最原始發布的24小時后失效,以盡量減少系統中的陳舊信息。
最后,為了維持<鍵,值>對在發布-搜索生命周期中的一致性,我們要求任何時候節點w擁有一個新節點u,u比w更接近w中的一些<鍵,值>對。w將復制這些<鍵,值>對給u并且不從自己的數據庫中刪除。
為了查找到一個<鍵,值>對,節點首先查找k個ID與鍵接近的節點。然而,值查詢使用FIND_VALUE而不是FIND_NODE RPC。 而且,只要任何節點返回了值,則這個過程立即結束。為了緩存(caching)的緣故,只要一個查詢成功了,這個請求節點將會把這個<鍵,值>對存儲到它擁有的最接近的并且沒能返回值的節點上。
由于這個拓撲的單向性,對相同的鍵的以后的搜索將很有可能在查詢最接近節點前命中已緩存的項。對于一個特定的鍵,經過多次的查找和傳播,系統可能在許多的節點上都緩存了這個鍵。為了避免“過度緩存”,我們設計了一個<鍵,值>對在任何節點的數據庫中的存活時間與當前節點和與鍵ID最接近的節點ID之間的節點數成指數級的反比例關系。簡單的剔除最久未聯系節點會導致相似的生存時間分布,沒有很自然的方法來選擇緩存大小,因為節點不能提前知道系統將會存儲多少個值。
???
一般來說,由于存在于節點之間的查詢的通信,桶會保持不停地刷新。為了避免當沒有通信時的病態情況,每個節點對在一個小時內沒有做過節點查詢的桶進行刷新,刷新意味著在桶的范圍內選擇一個隨機ID然后為這個ID做節點搜索。
為了加入到這個網絡中,節點u必須與一個已經加入到網絡中的節點w聯系。u把w加入到合適的桶中,然后u為自己的節點ID做一次節點查找。最后,節點u刷新所有比最接近的鄰居節點更遠的K-桶。在這個刷新過程中,節點u進行了兩項必需的工作:既填充了自己的K-桶,又把自己插入到了其它節點的K-桶中。
?
3
.驗證概述
為了驗證我們系統中的特有的函數,我們必須證實絕大多數的操作花費
[
log
n]
+
c
的時間開銷,并且c是一個比較小的常數,并且
<
鍵,值>查找將會以很高的概率返回一個存儲在系統中的鍵。
我們首先做一些定義。對于一個覆蓋距離的范圍為
[
2
i
,
2
i
+1)
的
K-
桶,定義這個桶的索引號為i。定義節點的深度h為160-i,其中i是最小的非空的桶的索引號。定義在節點x中節點y的桶高度為y將插入到x的桶的索引號減去x的最不重要的空桶的索引號。由于節點ID是隨機選擇的,因此高度的不統一分布是不太可能的。因此,在非常高的概率下,任意一個給定節點的高度在log n之內,其中n是系統中的節點數。而且,對于一個ID,最接近節點在第k接近的節點中的桶高度很有可能是在常數log k之內。
下一步我們將假設一個不變的條件,那就是每個節點的每個K-桶包含至少一個節點的聯系信息,如果這個節點存在于一個合適的范圍中。有了這個假設,我們可以發現節點的查找過程是正確的并且時間開銷是指數級的。假設與目標ID最接近的節點的深度是h。如果這個節點的h個最有意義的K-桶都是非空的,查詢過程在每一步都可以查找到一個到目標節點的距離更接近一半的節點(或者說距離更近了一個bit),因此在
h
-
log
k
步后目標節點將會出現。
如果這個節點的一個K-桶是空的,可能是這樣的一種情況,目標節點恰好在空桶對應的距離范圍之內。這種情況下,最后的幾步并不能使距離減半。然而,搜索還是能正確的繼續下去就像鍵中與空桶相關的那個位已經被置反了。因此,查找算法總是能在
h
-
log
k
步后
返回最接近節點。而且,一旦最接近節點已經找到,并行度會從а擴展到k。尋找到剩下的k-1個最接近節點的步數將不會超過最接近節點在第k接近節點中的桶高度,即不太可能超過log k加上一個常數。
為了證實前面的不變條件的正確性,首先考慮桶刷新的效果,如果不變條件成立。在被刷新后,一個桶或者包含k個有效節點,或者包含在它范圍內的所有節點,如果少于k個節點存在的話(這是從節點的查找過程的正確性而得出來的。)新加入的節點也會被插入到任何沒有滿的桶中去。因此,唯一違反這個不變條件的方法就是在一個特別的桶的范圍內存在k+1個活更多的節點,并且桶中的k個節點在沒有查找或刷新的干涉下全部失效。然而,k值被精確的選擇以保證使所有節點在一小時內(最大的刷新時間)全都失效的概率足夠小。
實際上,失敗的概率比k個節點在1小時內全都離開的概率小得多,因為每個進入或外出的請求消息都會更新節點的桶。這是異或運算的對稱性產生的,因為在一次進入或外出的請求中,與一個給定節點通信的對端節點的ID在該節點的桶范圍之內的分布是非常均勻的。
而且,即使這個不變條件在單個節點的單個桶中的確失效了,這也只影響到運行時間(在某些查詢中添加一個跳數),并不會影響到節點查找的正確性。只有在查找路徑中的k個節點都必須在沒有查找或刷新的干涉下在相同的桶中丟失k個節點,才可能造成一次查找失敗。如果不同的節點的桶沒有重疊,這種情況發生的概率是2-k*k。否則,節點出現在多個其它的節點的桶中,這就很可能會有更長的運行時間和更低概率的失敗情況。
現在我們來考慮下<鍵,值>對的恢復問題。當一個<鍵,值>對發布時,它將在k個與鍵接近的節點中存儲。同時每隔一小時將重新發布一次。因為即使是新節點(最不可靠的節點)都有1/2的概率持續存活一個小時,一個小時后<鍵,值>對仍然存在于k個最接近節點中的一個上的概率是1-2-k
。
這個性質并不會由于有接近鍵的新節點的插入而改變,因為一旦有這樣的節點插入,它們為了填充它們的桶將會與他們的最接近的那些節點交互,從而收到附近的它們應該存儲的<鍵,值>對。當然,如果這k個最接近鍵的節點都失效了,并且這個<鍵,值>對沒有在其它任何地方緩存,Kademlia將會丟失這個<鍵,值>對。
?
4
.討論
我們使用的基于異或拓撲的路由算法與Pastry [1], Tapestry [2]的路由算法中的第一步和 Plaxton的分布式搜索算法都非常的相似。然而,所有的這三個算法,當他們選擇一次接近目標節點b個bit的時候都會產生問題(為了加速的目的)。如果沒有異或拓撲,我們還需要一個額外的算法結構來從與目標節點擁有相同的前綴但是接下來的b個bit的數字不同的節點找到目標節點。所有的這三個算法在解決這個問題上采取的方法都是各不相同的,每個都有其不足之處;它們在大小為
O
(2
b
log
2
b
n
)
的主表之外
都
另外
需要
一個
大小為
O
(2
b
)
的
次要路由表,這增加了自舉和維護的開支,使協議變的更加復雜了,而且對于Pastry和Tapestry來說阻止了正確性與一致性的正式分析。Plaxton雖然可以得到證實,但在像點對點(P2P)網絡中的極易失效的環境中不太適應。
相反,Kademlia則非常容易的以不是2的基數被優化。我們可以配置我們的桶表來使每一跳b個bit的速度來接近目標節點。這就要求滿足一個條件,那就是任意的
0
< j < 2b
和
0
≤
i <
160
/b
,
在與我們的距離為[j2160-(i+1)b, (j+1)2160-(i+1)b]
的范圍內就要有一個桶,這個有實際的項的總量預計不會超過個桶。目前的實現中我們令b=5。
?
5
.總結
使用了新穎的基于異或運算的拓撲,Kademlia是第一個結合了可證實的一致性和高性能,最小時延路由,和一個對稱,單向的拓撲的點對點(P2P)系統。此外,Kademlia引入了一個并發參數,а,這讓人們可以通過調整帶寬的一個常數參數來進行異步最低時延的跳選擇和不產生時延的失效恢復。最后,Kademlia是第一個利用了節點失效與它的已運行時間成反比這個事實的點對點(P2P)系統。
?
參考文獻
[1] A. Rowstron and P. Druschel. Pastry: Scalable, distributed object location and routing for large-scale peer-to-peer systems. Accepted for Middleware, 2001, 2001. http://research.microsoft.com/?antr/pastry/.
?
[2] Ben Y. Zhao, John Kubiatowicz, and Anthony Joseph. Tapestry: an infrastructure for fault-tolerant wide-area location and routing. Technical Report UCB/CSD-01-1141, U.C. Berkeley, April 2001.
?
[3] Andr′ea W. Richa C. Greg Plaxton, Rajmohan Rajaraman. Accessing nearby copies of replicated objects in a distributed environment. In Proceedings of the ACM SPAA, pages 311–320, June 1997.
?
[4] Stefan Saroiu, P. Krishna Gummadi and Steven D. Gribble. A Measurement Study of Peer-to-Peer File Sharing Systems. Technical Report UW-CSE-01-06-02, University of Washington, Department of Computer Science and Engineering, July 2001.
?
[5] Ion Stoica, Robert Morris, David Karger, M. Frans Kaashoek, and Hari Balakrishnan. Chord: A scalable peer-to-peer lookup service for internet applications. In Proceedings of the ACM SIGCOMM ’01 Conference, San Diego, California, August 2001.