【我的区块链之路】- 理解传统Kademlia和以太坊Kademlia网络
【轉載請標明出處】:https://blog.csdn.net/qq_25870633/article/details/81939101?
本文章參考自:
http://www.yeolar.com/note/2010/03/21/kademlia/#id13? ??Kademlia協議原理簡介
https://keeganlee.me/post/blockchain/20180313? ? ? ? ? ? ? ?詳解區塊鏈P2P網絡
??
大家好,今天我們來說一說以太坊的Kad網絡;在此之前我們先來聊一聊少部分P2P方面的知識,P2P 主要存在四種不同的網絡模型,也代表著 P2P 技術的四個發展階段:集中式、純分布式、混合式和結構化模型。
- 集中式:即存在一個中心節點保存了其他所有節點的索引信息,索引信息一般包括節點 IP 地址、端口、節點資源等。
? ? ? ? ? ? ?優點:就是結構簡單、實現容易。
? ? ? ? ? ? ?缺點:由于中心節點需要存儲所有節點的路由信息,當節點規模擴展時,就很容易出現性能瓶頸;而且也存在單點故障問題。
- 純分布式:移除了中心節點,在 P2P 節點之間建立隨機網絡,就是在一個新加入節點和 P2P 網絡中的某個節點間隨機建立連接通道,從而形成一個隨機拓撲結構。 ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ?新節點加入該網絡的實現方法也有很多種,最簡單的就是隨機選擇一個已經存在的節點并建立鄰居關系。像比特幣的話,則是使用 DNS 的方式來查詢其他節點,DNS 一般是硬編碼到代碼里的,這些 DNS 服務器就會提供比特幣節點的 IP 地址列表,從而新節點就可以找到其他節點建立連接通道。新節點與鄰居節點建立連接后,還需要進行全網廣播,讓整個網絡知道該節點的存在。
? ? ? ? ? ? ? ? ? ?全網廣播的方式:該節點首先向鄰居節點廣播,鄰居節點收到廣播消息后,再繼續向自己的鄰居節點廣播,以此類推,從而廣播到整個網絡。這種廣播方法也稱為泛洪機制。純分布式結構不存在集中式結構的單點性能瓶頸問題和單點故障問題,具有較好的可擴展性,但泛洪機制引入了新的問題,主要是可控性差的問題:
? ? ? ? ? ? ? ? ? 【?一是】:容易形成泛洪循環,比如節點 A 發出的消息經過節點 B 到 節點 C,節點 C 再廣播到節點 A,這就形成 了一個循環;
? ? ? ? ? ? ? ? ? 【二是】:響應消息風暴問題,如果節點 A 想請求的資源被很多節點所擁有,那么在很短時間內,會出現大量節點同時向節點 A 發送響應消息,這就可能會讓節點 A 瞬間癱瘓。
- 混合式:混合了集中式和分布式結構,如下圖所示,網絡中存在多個超級節點組成分布式網絡,而每個超級節點則有多個普通節點與它組成局部的集中式網絡。一個新的普通節點加入,則先選擇一個超級節點進行通信,該超級節點再推送其他超級節點列表給新加入節點,加入節點再根據列表中的超級節點狀態決定選擇哪個具體的超級節點作為父節點。這種結構的泛洪廣播就只是發生在超級節點之間,就可以避免大規模泛洪存在的問題。在實際應用中,混合式結構是相對靈活并且比較有效的組網架構,實現難度也相對較小,因此目前較多系統基于混合式結構進行開發實現。其實,比特幣網絡如今也是這種結構。
?
? ? ? 圖片丟失 ...
- 結構化 P2P 網絡:它也是一種分布式網絡結構,但與純分布式結構不同。純分布式網絡就是一個隨機網絡,而結構化網絡則將所有節點按照某種結構進行有序組織,比如形成一個環狀網絡或樹狀網絡。而結構化網絡的具體實現上,普遍都是基于?DHT(Distributed Hash Table,分布式哈希表)?算法思想。DHT 只是提出一種網絡模型,并不涉及具體實現,主要想解決如何在分布式環境下快速而又準確地路由、定位數據的問題。具體的實現方案有 Chord、Pastry、CAN、Kademlia 等算法,其中?Kademlia?也是以太坊網絡的實現算法,很多常用的 P2P 應用如 BitTorrent、電驢等也是使用 Kademlia。
?P2P 網絡中,可以抽象出兩種空間:資源空間和節點空間。資源空間就是所有節點保存的資源集合,節點空間就是所有節點的集合。對所有資源和節點分別進行編號,如把資源名稱或內容用 Hash 函數變成一個數值(這也是 DHT 常用的一種方法),這樣,每個資源就有對應的一個 ID,每個節點也有一個 ID,資源 ID 和節點 ID 之間建立起一種映射關系,比如,將資源 n數值 的所有索引信息存放到 n數值節點上,那要搜索資源 n 時,只要找到節點 n 即可,從而就可以避免泛洪廣播,能更快速而又準確地路由和定位數據?!?span style="color:#7c79e5;">當然,在實際應用中,資源 ID 和節點 ID 之間是無法做到一一對應的(因為無法確保數值為100的資源ID可以在全網中找到數值也為100的節點ID),通常的解決方案是,但因為 ID 都是數字,就存在大小關系或偏序關系等來決定數值為 100 的資源ID應該存放到那些離數值 100 最近的k個節點上,基于這些關系就能建立兩者的映射關系】。這就是 DHT 的核心思想。DHT 算法在資源編號和節點編號上就是使用了分布式哈希表,使得資源空間和節點空間的編號有唯一性、均勻分布式等較好的性質,能夠適合結構化分布式網絡的要求。
好了,廢話逼逼了太多了(咳咳,應該是抄了人家的太多了),現在我們來說一說傳統kad網絡和以太坊的kad網絡,【因為他咩的,以太坊是用來做節點發現的,而類似于Bittorrent之類的除了可能要做節點發現外還需要做資源發現的】
來,我們來看一看 Kad是個什么樣的東東:
在kad網絡中我們是通過 兩個節點ID的異或運算得出來的值來算節點間的距離的,而不是依靠物理距離、路由器跳數來衡量的(因為這樣需要做很多的計算才可以知道對方的遠近,效率相當的慢),而通過異或這類的計算可以快速的精準的定位到對方節點
節點的距離:
異或操作也是單向性的。對于任意給定的節點?x?和距離?Δ≥0?,總會存在一個精確的節點?y?,使得?d(x,y)=Δ。
單向性也確保了對于同一個 key 值的所有查詢都會逐步收斂到同一個路徑上,而不管查詢的起始節點位置如何。這樣,只要沿著查詢路徑上的節點都緩存這個?<key,value>?對,就可以減輕存放熱門 key 值節點的壓力,同時也能夠加快查詢響應速度。【這句話的理解是:把<key, value> 均勻的放置到 和 key 的數值相近的 某一部分節點上,這樣紙的話我們在查找key時,總能命中這些節點上,這樣紙減輕了把 <key, value> 放置到 某單個節點上所造成的訪問壓力,及能夠加快查詢到 value 的速度】;所以,在Kad網絡中ID越靠近key的節點區域,該條目保存的份數就越多,存儲得也越集中;事實上,為了實現較短的查詢響應 延遲,在條目查詢的過程中,任一條目可被cache到任意節點之上;同時為了防止過度cache、保證信息足夠新鮮,必須考慮條目在節點上存儲的時效性: 越接近目標結點N,該條目保存的時間將越長,反之,其超時時間就越短;保存在目標節點之上的條目最多能夠被保留24小時,如果在此期間該條目被其發布源重 新發布(每個節點都會把自身的資源每個固定時間重新發布給臨近節點)的話,其保存時間還可以進一步延長。
節點的狀態:
在 Kad 網絡中,所有節點都被當作一顆二叉樹的葉子【這個信息是以各個節點本地的k-bucket所記錄的,可能每個節點的k-bucket所記錄的內容不盡一樣,但是大家所維護的k-bucket 組成的整網的節點狀態既是這樣】,并且每一個節點的位置都由其 ID 值的最短前綴唯一的確定?!具@句話我們先記下,不明白也沒關系,后面的內容會讓我們明白】,對于任意一個節點,都可以把這顆二叉樹分解為一系列連續的,不包含自己的子樹。最高層的子樹,由整顆樹不包含自己的樹的另一半組成,下一層子樹由剩下部分不包含自己的一半組成 【這句話不明白? 沒關系, 我們看圖, 對于節點 0011 來說,在它本地的 k-bucket中的節點形成的樹,應該是【1】(最高層的子樹,由整顆樹不包含自己的樹的另一半組成) 這句話描述的,而【2】【3】【4】(下一層子樹由剩下部分不包含自己的一半組成) 就是這句話描述的.】;依此類推,直到分割完整顆樹。比如節點0011如何進行子樹的劃分,如圖:
?
【注意】這里我們解釋下,為什么樹是這樣子的,想象下 假設對于兩個nodeId具備 很長 (前綴 0 或者 前綴 1?)的公共前綴來說,他們做 XOR 運算后,得到的值肯定越小,說明一個事實前綴相同越多的nodeId他們的距離越近。所以上面的樹是 1111 的在左邊, 0000 的在右邊。
其中,虛線包含的部分就是各子樹,由上到下各層的前綴分別為 1,01,000,0010 。Kad 協議確保每個節點知道其各子樹的至少一個節點。在這個前提下,每個節點都可以通過ID值來找到任何一個節點。這個路由的過程是通過所謂的 XOR(異或)距離得到的。
下面演示了節點0011如何通過連續查詢來找到節點1110的。節點0011通過在逐步底層的子樹間不斷學習并查詢最佳節點,獲得了越來越接近的節點,最終收斂到目標節點上:
由于網上很多說法都是互抄的,其實上光看這個圖還是不能明白,如何的收斂到目標節點:是這樣紙的,首先,需要知道請求發起節點?0011 和 目標節點?1110 的 距離 n ,然后從 請求節點 0011 的本地 k-bucket 的 n 桶中返回 k個 節點信息,然后 0011 再給這幾個節點廣播,如果 這幾個節點中剛好有距離是 n 的節點,那么就會用 0011 ,否則,就從這些節點的bendi k-bucket 中繼續上述操作,直到找到 1110 節點,所以這個過程是個遞歸的過程。
K桶:
Kad 的路由表是通過一些稱之為 K 桶的表格構造起來的,傳統的kad的k-bucket 的數目取值為??0≤i≤160 (這里為什么是 160 ?因為使用的 nodeId就是 160 bit,那么 兩個 nodeId 做XOR 運算得到的最大值就是 160);每個節點都保存有一些和自己距離范圍在區間?[2^i,2^i+1)?內的一些節點信息 (用 2 為底是因為每一個為1的bit代表一個2^x啊,主要還是和 XOR 的運算相關),這些信息由一些 (IP address,UDP port,Node ID) 數據列表構成(Kad 網絡是靠 UDP 協議交換信息的)。每一個這樣的列表都稱之為一個 K 桶,每一個list(k-桶)中最多存放k個對端節點信息;【注意】此處的k與上文所提到的條目被存放的最近節點數含義是一致的;每一個 list中的對端節點信息均按訪問時間排序,最早訪問的在list頭部,而最近新訪問的則放在list的尾部。
如圖:
表格形式如:
【注意】表格中的 0-k, 1-k 之類的下標是指 [距離]-[第k個數據]。所以每個不同距離的 bucket 中裝的數據最多只能是 k 個.
不過通常來說當 i 值很小時,K 桶通常是空的(也就是說沒有足夠多的節點,比如當 i = 0 時,就最多可能只有1項);而當 i 值很大時,其對應 K 桶的項數又很可能會超過 k 個(當然,覆蓋距離范圍越廣,存在較多節點的可能性也就越大),這里 k 是為平衡系統性能和網絡負載而設置的一個常數,是個經驗取值,但必須是偶數,比如 k = 20。在 BitTorrent 的實現中,取值為 k = 8。
經過證明,對于一個有 N 個節點的 Kad 網絡,最多只需要經過?logN?步查詢,就可以準確定位到目標節點。
K-Bucket的更新機制:
當節點 x 收到一個 RPC 消息時,發送者 y 的 IP 地址就被用來更新對應的 K 桶,具體步驟如下:
K 桶的更新機制非常高效的實現了一種把最近看到的節點更新的策略,除非在線節點一直未從 K 桶中移出過。也就是說在線時間長的節點具有較高的可能性繼續保留在 K 桶列表中;這是有依據得的:在線時間長一點的節點更值得我們信任,因為它在下一個小時以內保持在線的可能性將比我們最新訪問的節點更大?這對應 Kad 網絡的穩定性和減少網絡維護成本(不需要頻繁構建節點的路由表)帶來很大好處。
【好處】這種機制的另一個好處是能在一定程度上防御 DOS 攻擊,因為只有當老節點失效后,Kad 才會更新 K 桶的信息,這就避免了通過新節點的加入來泛洪路由信息。
為了防止 K 桶老化,所有在一定時間之內無更新操作的 K 桶,都會分別從自己的 K 桶中隨機選擇一些節點執行 RPC_PING 操作。
上述這些 K 桶機制使 Kad 緩和了流量瓶頸(所有節點不會同時進行大量的更新操作),同時也能對節點的失效進行迅速響應。
Kademlia的協議操作類型:
Kademlia 協議包括四種遠程 RPC 操作:PING、STORE、FIND_NODE、FIND_VALUE。
PING?操作的作用是探測一個節點,用以判斷其是否仍然在線。
STORE?操作的作用是通知一個節點存儲一個?<key,value>?對,以便以后查詢需要。
FIND_NODE?操作使用一個 160 bit 的 ID 作為參數。本操作的接受者返回它所知道的更接近目標 ID 的 K 個節點的 (IP address, UDP port, Node ID) 信息。
這些節點的信息可以是從一個單獨的 K 桶獲得,也可以從多個 K 桶獲得(如果最接近目標 ID 的 K 桶未滿)。不管是哪種情況,接受者都將返回 K 個節點的信息給操作發起者。但如果接受者所有 K 桶的節點信息加起來也沒有 K 個,則它會返回全部節點的信息給發起者。
FIND_VALUE?操作和 FIND_NODE 操作類似,不同的是它只需要返回一個節點的 (IP address, UDP port, Node ID) 信息。如果本操作的接受者收到同一個 key 的 STORE 操作,則會直接返回存儲的 value 值。
?
【注意】為了防止偽造地址,在所有 RPC 操作中,接受者都需要響應一個隨機的 160 bit 的 ID 值 【即 接受者自己的ID】。另外,為了確信發送者的網絡地址,PING 操作還可以附帶在接受者的 RPC 回復信息中? (就是說, 可以在上述 4種操作中? 接受者回復 發送者時,可以攜帶上 接受者對 發送者的 PING, 以此校驗 發送者是否還健在)。
?
路由查找機制:
Kad 技術的最大特點之一就是能夠提供快速的節點查找機制,并且還可以通過參數進行查找速度的調節;
假如節點 x 要查找 ID 值為 t 的節點,Kad 按照如下遞歸操作步驟進行路由查找:
【注意】:這里用“最接近”這個說法,是因為 ID 值為 t 的節點不一定存在網絡中,也就是說 t 沒有分配給任何一臺電腦。所以,Kad之所以沒有把節點查詢過程嚴格地定義成為僅僅只查詢單個目標節點的過程,這主要是因為Kad網絡并沒有對節點的上線時間作出 任何前提假設,因此在多數情況下我們并不能肯定需要查找的目標節點一定在線或存在
這里?α?也是為系統優化而設立的一個參數,就像 K 一樣。在 BitTorrent 實現中,取值為?α=3?。
當?α=1?時,查詢過程就類似于 Chord 的逐跳查詢過程,如圖:
?
整個路由查詢過程是遞歸操作的,其過程可用數學公式表示為:
n0=x?(即查詢操作的發起者)
N1=find??noden0(t)
N2=find??noden1(t)
... ...
Nl=find??nodenl?1(t)
這個遞歸過程一直持續到?Nl=t?,或者?Nl?的路由表中沒有任何關于 t 的信息,即查詢失敗。
由于每次查詢都能從更接近 t 的 K 桶中獲取信息,這樣的機制保證了每一次遞歸操作都能夠至少獲得距離減半(或距離減少 1 bit)的效果,從而保證整個查詢過程的收斂速度為?O(logN)?,這里 N 為網絡全部節點的數量?!疽驗?#xff1a;對于一個有 N 個節點的 Kad 網絡,最多只需要經過?logN?步查詢】
?
<key, Value>的查找:
?
當節點 x 要查詢?<key,value>?對時,和查找節點的操作類似,x 選擇 k 個 ID 值最接近 key 值的節點,執行 FIND_VALUE 操作,并對每一個返回的新節點重復執行 FIND_VALUE 操作,直到某個節點返回 value 值。
一旦 FIND_VALUE 操作成功執行,則?<key,value>?對數據會緩存在沒有返回 value 值的最接近的節點上。這樣下一次查詢相同的 key 時就會更加快速的得到結果。通過這樣的方式,熱門?<key,value>?對數據的緩存范圍就逐步擴大,使系統具有極佳的響應速度。cache的超時時間為節點-key之間的距離越遠則超時時間越短。(【因為】 cache 為存活24小時,但是目標節點上的內容時每1小時向其他最近節點重新發布<key, value>使得數據的超時時間得以刷新,而遠離目標節點的節點的數據存活時間當然就可能不會被重新發布到,所以也就是數據緩存的超時時間和節點的距離成反比咯)
數據的存放:
存放?<key,value>?對數據的過程為:
另外,為了保證數據發布、搜尋的一致性,規定在任何時候,當節點 w 發現新節點 u 比 w 上的某些?<key,value>?對數據(即 對Key更接近)更接近,則 w 把這些?<key,value>?對數據復制到 u 上,但是并不會從 w 上刪除(超時時間到了,且遠離了key則后續會自動被刪除)
節點加入和離開:
如果節點 u 要想加入 Kad 網絡,它必須要和一個已經在 Kad 網絡的節點,比如 w,取得聯系。
u 首先把 w 插入自己適當的 K 桶中,然后對自己的節點 ID 執行一次 FIND_NODE 操作 (向 w?發布 查找 u 的 FIND_NODE 請求),然后根據接收到的信息更新自己的 K 桶內容。通過對自己鄰近節點由近及遠的逐步查詢,u 完成了仍然是空的 K 桶信息的構建,同時也把自己的信息發布到其他節點的 K 桶中。
所以, 節點 u?必須做三件事,【其一】不管通過何種途徑,獲知一個已經加入Kad網絡的節點信息(我們可以稱之為節點 w),并將其加入自己的k-buckets;【其二】向該w節點發起一次針對自己ID (u) 的節點查詢請求,從而通過節點w獲取一系列與自己距離鄰近的其他節點的信 息;【最后】刷新所有的k-bucket,保證自己所獲得的節點信息全部都是新鮮的
k-bucket和二叉樹:? (k-bucket的分裂)
還記得在 節點狀態 那一小節中我們講了,在 Kad 網絡中,所有節點都被當作一顆二叉樹的葉子,并且每一個節點的位置都由其 ID 值的最短前綴唯一的確定。那么我們下面就講一講這個是怎么個設計:
在 Kad 網絡中,每個節點的路由表都表示為一顆二叉樹,葉子節點為 K 桶 (因為k桶里面裝的也是節點),K 桶存放的是有相同 ID 前綴的節點信息,而這個前綴就是該 K 桶在二叉樹中的位置【前綴值確定了節點應該放到 什么距離的k-桶中】。這樣,每個 K 桶都覆蓋了 ID 空間的一部分,全部 K 桶的信息加起來就覆蓋了整個 160 bit 的 ID 空間,而且沒有重疊。
如:0000111 和 0000001是屬于同一個k-桶的兩個節點,其中對于他們的前綴可能是 0000 ; 而對于 1111111 和 1011111 來說 他們所屬于前綴為 1 的這個 k-桶,總之當前節點 N 來說,它本地的全部?k-桶 組成了 節點N 所感知的全網節點的拓撲,當然這肯定只是真實的全網節點拓撲的一部分而已,但是對于節點N來說卻是他所認為的全網拓撲,這樣紙的各個節點本地的k-桶所組成的各個節點所認為的全網拓撲組成了一張邏輯上的真實全網拓撲。
下面是對某節點本地的k-桶進行二叉樹的分割原理,以節點 u 為例,其路由表的生成過程為:
上述過程不斷重復,最終會達到距離近的節點的信息多,距離遠的節點的信息少的結果【因為隨著新節點的加入需要對原有某包含了當前節點u的k-桶進行拆分,可知,圖中越來越多的節點加入k-桶中,隨著k-桶的拆分,我們知道離目標節點越近的二叉樹(k-
桶)越多】,保證了路由查詢過程能快速收斂。具體原理為圖中所示:
當 K 桶 010 滿了之后,由于其覆蓋范圍包含了節點 0100 的 ID,故該 K 桶分裂為兩個新的 K 桶:0101 和 0100,原 K 桶 010 的信息會根據其其前綴值重新分布到這兩個新的 K 桶中。注意,這里并沒有使用 160 bit 的 ID 值表示法,只是為了方便原理的演示,實際 Kad 網絡中的 ID 值都是 160 bit 的。
節點離開:
節點離開 Kad 網絡不需要發布任何信息,Kademlia 協議的目標之一就是能夠彈性工作在任意節點隨時失效的情況下。為此,Kad 要求每個節點必須周期性? 【一般是: 每小時】 的發布全部自己存放的?<key,value>?對數據,并把這些數據緩存在自己的 k 個最近鄰居處,這樣存放在失效節點的數據會很快被更新到其他新節點上。所以有節點離開了,那么就離開了,而且節點中的k-桶刷新機制也能保證會把已經不在線的節點信息從自己本地k-桶中移除。
以太坊的Kad網絡:
我們知道常規的kad網絡節點的本地維護者 最多 160 個k-桶的信息,每個桶中 最多有K個臨近節點的信息 (K 為經驗取值, K 取值的原則是 不管什么時刻都能確保這K個節點中有 一個節點在網絡中是存活的,通常取值 k == 20 為最好);以太坊的節點本地有 256 個k-bucket? (代碼中看了是 256 + 1 == 257 個),每個里面最多 16 個臨近節點的信息;
而且??傳統的kad網絡除了需要節點發現外還需要資源發現;而以太坊中主要是主要節點發現,所以kad協議的操作類型和傳統的不一樣:
節點的發現:
鄰居節點發現流程說明:
系統第一次啟動隨機生成本機節點NodeId,記為LocalId,生成后將固定不變,本地節點記為local-eth。
系統讀取公共節點信息 【注意: 這些就是 種子節點】,ping-pong握手完成后,將其寫入K桶。
系統每隔7200ms刷新一次K桶。
刷新K桶流程如下:
a.??????隨機生成目標節點Id,記為TargetId,從1開始記錄發現次數和刷新時間。
b.??????計算TargetId與LocalId的距離,記為Dlt,然后從該距離的桶中取出所有kadId。
c.??????K桶中節點的NodeId記為KadId,計算KadId與TargetId的距離,記為Dkt
d.??????找出K桶中Dkt 小于 Dlt的節點,記為k桶節點,向k桶節點發送FindNODE命令,FindNODE命令包含TargetId
e.??????K桶節點收到FindNODE命令后,同樣執行b-d的過程【這時候,上一個KadId成為了新的LocalId了】,將從K桶中找到的節點使用Neighbours命令發回給本機節點。
f.???????本機節點收到Neighbours后,將收到的節點寫入到K桶中。
g.??????若搜索次數不超過8次,刷新時間不超過600ms,則返回到b步驟循環執行。
【注意】步驟d中的做法是, 向 到target距離 比 local到target距離更小的 node發起 FIND_NODE,因為 Dkt < Dlt時, 說明該k-bucket中的這個node到target的距離比local到target的距離更近,那么我們要查找target信息肯定是優先往這些node發起FIND_NODE是最合適的.
鄰居節點網絡拓撲及刷新機制:
?
1 TargetId為隨機生成的虛擬節點ID。
2?以太坊Kad網絡與傳統Kad網絡的區別:
以太坊節點在發現鄰居節點的8次循環中,所查找的節點均在距離上向隨機生成的TargetId收斂。
傳統Kad網絡發現節點時,在距離上向目標節點本身收斂。
至此,我們先告一段落,放心,后面發現有不足的我都會及時補充~
總結
以上是生活随笔為你收集整理的【我的区块链之路】- 理解传统Kademlia和以太坊Kademlia网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2.用一个简单的pk小游戏深刻理解继承
- 下一篇: 什么是大数据?它存在的意义和用途是什么?