近年來,區(qū)塊鏈技術(shù)(部分人更愿意稱之為分布式賬本技術(shù))的走紅將分布式技術(shù)的概念帶入大眾的視野。區(qū)塊鏈技術(shù)之所以備受追捧,一方面是其展現(xiàn)了一種在計(jì)算機(jī)的輔助下,人類可以以無中心、無權(quán)威、無層級的方式來進(jìn)行社會協(xié)作的美妙前景;另一方面,從物理上可論證,分布式的簡單協(xié)議,比中心化的復(fù)雜協(xié)議更為高效。分布式技術(shù)似乎能夠在帶來公平的同時(shí),還帶來效率。
要理解分布式技術(shù)并不困難,因?yàn)榉植际郊夹g(shù)并不高深,但其設(shè)計(jì)上往往巧妙得令人拍手稱贊。
本文介紹一種常見而巧妙的分布式技術(shù),Kademlia算法。
Kademlia算法是一種分布式存儲及路由的算法。什么是分布式存儲?試想一下,一所1000人的學(xué)校,現(xiàn)在學(xué)校突然決定拆掉圖書館(不設(shè)立中心化的服務(wù)器),將圖書館里所有的書都分發(fā)到每位學(xué)生手上(所有的文件分散存儲在各個(gè)節(jié)點(diǎn)上)。即是所有的學(xué)生,共同組成了一個(gè)分布式的圖書館。
在這種場景下,有幾個(gè)關(guān)鍵的問題需要回答。
1)關(guān)鍵問題
- 每個(gè)同學(xué)手上都分配哪些書。即如何分配存儲內(nèi)容到各個(gè)節(jié)點(diǎn),新增/刪除內(nèi)容如何處理。
- 當(dāng)你需要找到一本書,譬如《分布式算法》的時(shí)候,如何知道哪位同學(xué)手上有《分布式算法》(對1000個(gè)人挨個(gè)問一遍,“你有沒有《分布式算法》?”,顯然是個(gè)不經(jīng)濟(jì)的做法),又如何聯(lián)系上這位同學(xué)。即一個(gè)節(jié)點(diǎn)如果想獲取某個(gè)特定的文件,如何找到存儲文件的節(jié)點(diǎn)/地址/路徑。
接下來,讓我們來看看Kademlia算法如何巧妙地解決這些問題。
2)節(jié)點(diǎn)的要素
首先我們來看看每個(gè)同學(xué)(節(jié)點(diǎn))都有哪些屬性:
- 學(xué)號(Node ID,2進(jìn)制,160位)
- 手機(jī)號碼(節(jié)點(diǎn)的IP地址及端口)
每個(gè)同學(xué)會維護(hù)以下內(nèi)容:
- 從圖書館分發(fā)下來的書本(被分配到需要存儲的內(nèi)容),每本書當(dāng)然都有書名和書本內(nèi)容(內(nèi)容以<key, value>對的形式存儲,可以理解為文件名和文件內(nèi)容);
- 一個(gè)通訊錄,包含一小部分其他同學(xué)的學(xué)號和手機(jī)號,通訊錄按學(xué)號分層(一個(gè)路由表,稱為“k-bucket”,按Node ID分層,記錄有限個(gè)數(shù)的其他節(jié)點(diǎn)的ID和IP地址及端口)。
根據(jù)上面那個(gè)類比,可以看看這個(gè)表格:
(Hash的概念解釋,可參見百度百科-哈希算法)
關(guān)于為什么不是每個(gè)同學(xué)都有全量通訊錄(每個(gè)節(jié)點(diǎn)都維護(hù)全量路由信息):其一,分布式系統(tǒng)中節(jié)點(diǎn)的進(jìn)入和退出是相當(dāng)頻繁的,每次有變動(dòng)時(shí)都全網(wǎng)廣播通訊錄更新,通訊量會很大;其二,一旦任意一個(gè)同學(xué)被壞人綁架了(節(jié)點(diǎn)被黑客攻破),則壞人馬上就擁有了所有人的手機(jī)號碼,這并不安全。
3)文件的存儲及查找
原來收藏在圖書館里,按索引號碼得整整齊齊的書,以一種什么樣的方式分發(fā)到同學(xué)們手里呢?大致的原則,包括:1)書本能夠比較均衡地分布在同學(xué)們的手里,不會出現(xiàn)部分同學(xué)手里書特別多、而大部分同學(xué)連一本書都沒有的情況;2)同學(xué)想找一本特定的書的時(shí)候,能夠一種相對簡單的索引方式找到這本書。
Kademlia作了下面這種安排:
假設(shè)《分布式算法》這本書的書名的hash值是 00010000,那么這本書就會被要求存在學(xué)號為00010000的同學(xué)手上。(這要求hash算法的值域與node ID的值域一致。Kademlia的Node ID是160位2進(jìn)制。這里的示例對Node ID進(jìn)行了簡略)
但還得考慮到會有同學(xué)缺勤。萬一00010000今天沒來上學(xué)(節(jié)點(diǎn)沒有上線或徹底退出網(wǎng)絡(luò)),那《分布式算法》這本書豈不是誰都拿不到了?那算法要求這本書不能只存在一個(gè)同學(xué)手上,而是被要求同時(shí)存儲在學(xué)號最接近00010000的k位同學(xué)手上,即00010001、00010010、00010011…等同學(xué)手上都會有這本書。
同樣地,當(dāng)你需要找《分布式算法》這本書時(shí),將書名hash一下,得到 00010000,這個(gè)便是索書號,你就知道該找哪(幾)位同學(xué)了。剩下的問題,就是找到這(幾)位同學(xué)的手機(jī)號。
4)節(jié)點(diǎn)的異或距離
由于你手上只有一部分同學(xué)的通訊錄,你很可能并沒有00010000的手機(jī)號(IP地址)。那如何聯(lián)系上目標(biāo)同學(xué)呢?
一個(gè)可行的思路就是在你的通訊錄里找到一位擁有目標(biāo)同學(xué)的聯(lián)系方式的同學(xué)。前面提到,每位同學(xué)手上的通訊錄都是按距離分層的。算法的設(shè)計(jì)是,如果一個(gè)同學(xué)離你越近,你手上的通訊錄里存有ta的手機(jī)號碼的概率越大。而算法的核心的思路就可以是:當(dāng)你知道目標(biāo)同學(xué)Z與你之間的距離,你可以在你的通訊錄上先找到一個(gè)你認(rèn)為與同學(xué)Z最相近的同學(xué)B,請同學(xué)B再進(jìn)一步去查找同學(xué)Z的手機(jī)號。
上文提到的距離,是學(xué)號(Node ID)之間的異或距離(XOR distance)。異或是針對yes/no或者二進(jìn)制的運(yùn)算.
異或的運(yùn)算法則為:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同為0,異為1)
百度百科-異或
舉2個(gè)例子:
01010000與01010010距離(即是2個(gè)ID的異或值)為00000010(換算為十進(jìn)制即為2);
01000000與00000001距離為01000001(換算為十進(jìn)制即為26+1,即65);
如此類推。
那通訊錄是如何按距離分層呢?下面的示例會告訴你,按異或距離分層,基本上可以理解為按位數(shù)分層。設(shè)想以下情景:
以0000110為基礎(chǔ)節(jié)點(diǎn),如果一個(gè)節(jié)點(diǎn)的ID,前面所有位數(shù)都與它相同,只有最后1位不同,這樣的節(jié)點(diǎn)只有1個(gè)——0000111,與基礎(chǔ)節(jié)點(diǎn)的異或值為0000001,即距離為1;對于0000110而言,這樣的節(jié)點(diǎn)歸為“k-bucket 1”;
如果一個(gè)節(jié)點(diǎn)的ID,前面所有位數(shù)相同,從倒數(shù)第2位開始不同,這樣的節(jié)點(diǎn)只有2個(gè):0000101、0000100,與基礎(chǔ)節(jié)點(diǎn)的異或值為0000011和0000010,即距離范圍為3和2;對于0000110而言,這樣的節(jié)點(diǎn)歸為“k-bucket 2”;
……
如果一個(gè)節(jié)點(diǎn)的ID,前面所有位數(shù)相同,從倒數(shù)第n位開始不同,這樣的節(jié)點(diǎn)只有2(i-1)個(gè),與基礎(chǔ)節(jié)點(diǎn)的距離范圍為[2(i-1), 2i);對于0000110而言,這樣的節(jié)點(diǎn)歸為“k-bucket i”;
對上面描述的另一種理解方式:如果將整個(gè)網(wǎng)絡(luò)的節(jié)點(diǎn)梳理為一個(gè)按節(jié)點(diǎn)ID排列的二叉樹,樹最末端的每個(gè)葉子便是一個(gè)節(jié)點(diǎn),則下圖就比較直觀的展現(xiàn)出,節(jié)點(diǎn)之間的距離的關(guān)系。
回到我們的類比。每個(gè)同學(xué)只維護(hù)一部分的通訊錄,這個(gè)通訊錄按照距離分層(可以理解為按學(xué)號與自己的學(xué)號從第幾位開始不同而分層),即k-bucket1, k-bucket 2, k-bucket 3…雖然每個(gè)k-bucket中實(shí)際存在的同學(xué)人數(shù)逐漸增多,但每個(gè)同學(xué)在它自己的每個(gè)k-bucket中只記錄k位同學(xué)的手機(jī)號(k個(gè)節(jié)點(diǎn)的地址與端口,這里的k是一個(gè)可調(diào)節(jié)的常量參數(shù))。
由于學(xué)號(節(jié)點(diǎn)的ID)有160位,所以每個(gè)同學(xué)的通訊錄中共分160層(節(jié)點(diǎn)共有160個(gè)k-bucket)。整個(gè)網(wǎng)絡(luò)最多可以容納2^160個(gè)同學(xué)(節(jié)點(diǎn)),但是每個(gè)同學(xué)(節(jié)點(diǎn))最多只維護(hù)160 * k 行通訊錄(其他節(jié)點(diǎn)的地址與端口)。
5)節(jié)點(diǎn)定位
我們現(xiàn)在來闡述一個(gè)完整的索書流程。
A同學(xué)(學(xué)號00000110)想找《分布式算法》,A首先需要計(jì)算書名的哈希值,hash(《分布式算法》) = 00010000。那么A就知道ta需要找到00010000號同學(xué)(命名為Z同學(xué))或?qū)W號與Z鄰近的同學(xué)。
Z的學(xué)號00010000與自己的異或距離為 00010110,距離范圍在[24, 25),所以這個(gè)Z同學(xué)可能在k-bucket 5中(或者說,Z同學(xué)的學(xué)號與A同學(xué)的學(xué)號從第5位開始不同,所以Z同學(xué)可能在k-bucket 5中)。
然后A同學(xué)看看自己的k-bucket 5有沒有Z同學(xué):
- 如果有,那就直接聯(lián)系Z同學(xué)要書;
- 如果沒有,在k-bucket 5里隨便找一個(gè)B同學(xué)(注意任意B同學(xué),它的學(xué)號第5位肯定與Z相同,即它與Z同學(xué)的距離會小于24,相當(dāng)于比Z、A之間的距離縮短了一半以上),請求B同學(xué)在它自己的通訊錄里按同樣的查找方式找一下Z同學(xué):
-- 如果B知道Z同學(xué),那就把Z同學(xué)的手機(jī)號(IP Address)告訴A;
-- 如果B也不知道Z同學(xué),那B按同樣的搜索方法,可以在自己的通訊錄里找到一個(gè)離Z更近的C同學(xué)(Z、C之間距離小于23),把C同學(xué)推薦給A;A同學(xué)請求C同學(xué)進(jìn)行下一步查找。
Kademlia的這種查詢機(jī)制,有點(diǎn)像是將一張紙不斷地對折來收縮搜索范圍,保證對于任意n個(gè)學(xué)生,最多只需要查詢log2(n)次,即可找到獲得目標(biāo)同學(xué)的聯(lián)系方式(即在對于任意一個(gè)有[2(n?1), 2n)個(gè)節(jié)點(diǎn)的網(wǎng)絡(luò),最多只需要n步搜索即可找到目標(biāo)節(jié)點(diǎn))。
以上便是Kademlia算法的基本原理。以下再簡要介紹協(xié)議中的技術(shù)細(xì)節(jié)。
6)算法的三個(gè)參數(shù):keyspace,k和α
- keyspace
-- 即ID有多少位
-- 決定每個(gè)節(jié)點(diǎn)的通訊錄有幾層 - k
-- 每個(gè)一層k-bucket里裝k個(gè)node的信息,即<node ID, IP Adress, port>
-- 每次查找node時(shí),返回k個(gè)node的信息
-- 對于某個(gè)特定的data,離其key最近的k個(gè)節(jié)點(diǎn)被會要求存儲這個(gè)data - α
-- 每次向其他node請求查找某個(gè)node時(shí),會向α個(gè)node發(fā)出請求
7)節(jié)點(diǎn)的指令
Kademlia算法中,每個(gè)節(jié)點(diǎn)只有4個(gè)指令
- PING
-- 測試一個(gè)節(jié)點(diǎn)是否在線 - STORE
-- 要求一個(gè)節(jié)點(diǎn)存儲一份數(shù)據(jù) - FIND_NODE
-- 根據(jù)節(jié)點(diǎn)ID查找一個(gè)節(jié)點(diǎn) - FIND_VALUE
-- 根據(jù)KEY查找一個(gè)數(shù)據(jù),實(shí)則上跟FIND_NODE非常類似
8)k-bucket的維護(hù)及更新機(jī)制
- 每個(gè)bucket里的節(jié)點(diǎn)都按最后一次接觸的時(shí)間倒序排列
- 每次執(zhí)行四個(gè)指令中的任意一個(gè)都會觸發(fā)更新
- 當(dāng)一個(gè)節(jié)點(diǎn)與自己接觸時(shí),檢查它是否在K-bucket中
-- 如果在,那么將它挪到k-bucket列表的最底(最新)
-- 如果不在,PING一下列表最上面(最舊)的一個(gè)節(jié)點(diǎn)
-- a) 如果PING通了,將舊節(jié)點(diǎn)挪到列表最底,并丟棄新節(jié)點(diǎn)
-- b) 如果PING不通,刪除舊節(jié)點(diǎn),并將新節(jié)點(diǎn)加入列表
該機(jī)制保證了任意節(jié)點(diǎn)加入和離開都不影響整體網(wǎng)絡(luò)。
9)總結(jié)
Kademlia是分布式哈希表(Distributed Hash Table, DHT)的一種。而DHT是一類去中心化的分布式系統(tǒng)。在這類系統(tǒng)中,每個(gè)節(jié)點(diǎn)(node)分別維護(hù)一部分的存儲內(nèi)容以及其他節(jié)點(diǎn)的路由/地址,使得網(wǎng)絡(luò)中任何參與者(即節(jié)點(diǎn))發(fā)生變更(進(jìn)入/退出)時(shí),對整個(gè)網(wǎng)絡(luò)造成的影響最小。DHT可以用于構(gòu)建更復(fù)雜的應(yīng)用,包括分布式文件系統(tǒng)、點(diǎn)對點(diǎn)技術(shù)文件分享系統(tǒng)、合作的網(wǎng)頁高速緩存、域名系統(tǒng)以及實(shí)時(shí)通信等。
Kademlia算法在2002年由Petar Maymounkov 和 David Mazières 所設(shè)計(jì),以異或距離來對哈希表進(jìn)行分層是其特點(diǎn)。Kademlia后來被eMule、BitTorrent等P2P軟件采用作為底層算法。Kademlia可以作為信息安全技術(shù)的奠基之一。
Kademlia的優(yōu)點(diǎn)在于:
- 對于任意一個(gè)有[ 2(n?1) ,2??)個(gè)節(jié)點(diǎn)的網(wǎng)絡(luò),最多只需要n步搜索即可找到目標(biāo)節(jié)點(diǎn);
- K-bucket的更新機(jī)制一定程度上保持了網(wǎng)絡(luò)的活性和安全性。
參考文獻(xiàn)
wiki百科-分布式哈希表
wiki百科-Kademlia
Kademlia: A Peer-to-peer information system based on the XOR Metric
王子亭的Kademlia筆記
韓鋒.《區(qū)塊鏈的人工智能》.新星出版社《區(qū)塊鏈新經(jīng)濟(jì)藍(lán)圖及導(dǎo)讀》的譯后注