由于我們對P2P網(wǎng)絡(luò)和分布式系統(tǒng)感興趣,因此我們決定開始用Ruby從頭開始構(gòu)建自己的類似BitTorrent(BT)的系統(tǒng)。目前可用的版本是我們發(fā)布的alpha測試版本,提供了一個有效的概念驗(yàn)證:完整的分布式路由表,節(jié)點(diǎn)間的通信和基本的文件傳輸。在本文末列出了未來要新增的功能,我們將會繼續(xù)改進(jìn)Xorro。
本文記錄了我們構(gòu)建XorroP2P的過程。希望讀者閱讀本文后能對P2P系統(tǒng)有更深一層的認(rèn)識。
XorroP2P運(yùn)行界面
研究
作為嚴(yán)格意義上的P2P系統(tǒng)的最終用戶,我們開始了開發(fā)旅程,面臨著非常陡峭的學(xué)習(xí)曲線。我們必須進(jìn)行大量的研究來了解P2P相關(guān)的歷史和內(nèi)部組成。我們進(jìn)行了搜集和閱讀P2P相關(guān)資料,從舊到新依次有:Napster、Gnutella、Freenet。BitTorrent、IPFS……
集中式系統(tǒng)vs去中心化系統(tǒng)
需要理解的一個重要概念是集中式和去中心化系統(tǒng)之間的區(qū)別。第一代和第二代網(wǎng)絡(luò)架構(gòu)的比較有助于描述這兩者之間的區(qū)別。
Napster:集中式系統(tǒng)
Napster是一種P2P文件共享服務(wù),主要用于傳輸音樂文件,在1999~2001年非常流行,據(jù)估計,在鼎盛時期大約有8000萬注冊用戶。Napster的工作原理是讓所有節(jié)點(diǎn)都連接到中央索引服務(wù)器,該服務(wù)器包含所有關(guān)于誰擁有哪些文件的信息。
集中式P2P網(wǎng)絡(luò)的一個示例
因?yàn)槠浔旧頌榧惺降慕Y(jié)構(gòu),Napster中央服務(wù)器很容易遭受到攻擊,以及文件傳送的方式備受爭議,營運(yùn)兩年后在法院的判決下被迫關(guān)閉。除此之外,中央索引服務(wù)器還意味著存在單點(diǎn)故障,以及缺乏可擴(kuò)展性。
BitTorrent、Gnutella、Freenet:去中心化系統(tǒng)
下一代P2P網(wǎng)絡(luò)通過采用去中心化的模式避免了與Napster相同的命運(yùn)。
在像BitTorrent這樣的去中心化系統(tǒng)中,每臺計算機(jī)/節(jié)點(diǎn)都充當(dāng)客戶端和服務(wù)器,維護(hù)自己的文件查找索引片段。節(jié)點(diǎn)可以通過其他節(jié)點(diǎn)來查找文件的位置,免除了對中央服務(wù)器的依賴。
去中心化P2P網(wǎng)絡(luò)的一個示例
P2P文件共享系統(tǒng)的深入介紹
了解新一代P2P系統(tǒng)的優(yōu)點(diǎn)后,我們繼續(xù)深入研究它們的特性。幸運(yùn)的是,P2P網(wǎng)絡(luò)是已經(jīng)發(fā)展一段時間的技術(shù),因此網(wǎng)上有許多資源可供我們利用。其中分布式哈希表(distributedhashtables,DHT)是P2P網(wǎng)絡(luò)中最重要的技術(shù),因此分布式哈希表是當(dāng)前P2P網(wǎng)絡(luò)的基礎(chǔ)。我們花了很多時間進(jìn)行閱讀及研究白皮書、規(guī)范文檔、博文和Stackflow問答,在開始編程之前,我們要確保對分布式哈希表有深刻的了解。
我們找到的有用資源的列表在這里:https://xorro-p2p.github.io/resources/
功能的選擇
BitTorrent主要功能
經(jīng)過比較許多P2P網(wǎng)絡(luò)之后,我們最終鎖定了BitTorrent的一組功能,作為我們開發(fā)應(yīng)用的第一個版本的藍(lán)本。這些功能將在下文中將進(jìn)一步詳細(xì)描述。
分布式哈希表(DHT)
文件切分
節(jié)點(diǎn)既作為客戶端又作為服務(wù)器
演示
在深入了解XorroP2P的實(shí)現(xiàn)細(xì)節(jié)之前,請先查看下面的動圖,該動圖解說了文件下載過程的實(shí)際情況。
首先,清單文件(即BT種子,下同)會被下載,然后依據(jù)種子中列出的文件切分信息下載各個文件切片。下載完所有的切片后,將它們重新合并回原始文件。
分布式哈希表的實(shí)現(xiàn)
先評估幾種分布式哈希表的優(yōu)缺點(diǎn):Chord、Pastry、ApacheCassandra、Kademlia等等,再以此為考量作為我們選擇的依據(jù)。我們最終決定就其普及率、最簡單的遠(yuǎn)程過程調(diào)用和信息的自動傳播等優(yōu)點(diǎn),選擇了Kademlia。
事實(shí)證明,由于大量的新概念:節(jié)點(diǎn)、路由表、桶(buckets)、異或距離、路由算法、遠(yuǎn)程過程調(diào)用(remoteprocedurecalls,RPC)……,使得Kademlia的實(shí)施極具挑戰(zhàn)性。雖然當(dāng)時網(wǎng)上已經(jīng)有一些Ruby的實(shí)現(xiàn),但我們只依據(jù)規(guī)范和白皮書,因?yàn)槲覀円獜念^開始構(gòu)建分布式哈希表。
Kademlia
一個Kademlia網(wǎng)絡(luò)由許多節(jié)點(diǎn)組成。
每一個節(jié)點(diǎn)都有:
具有唯一的160位ID。
維護(hù)包含其他節(jié)點(diǎn)聯(lián)系信息的路由表。
維護(hù)較大的分布式哈希表中那些自己的段。
通過4個遠(yuǎn)程過程調(diào)用與其他節(jié)點(diǎn)通信。
每個節(jié)點(diǎn)的路由表被劃分為“桶”,每個桶包含與當(dāng)前節(jié)點(diǎn)的特定“距離”的節(jié)點(diǎn)的聯(lián)系信息。我們會在后面更詳細(xì)介紹關(guān)于距離的概念。
每個聯(lián)系信息都包含其他節(jié)點(diǎn)的ID、IP地址和端口號。
由于Xorro是一個文件共享應(yīng)用程序,因此分布式哈希表段將包含key/value對,其中,每個key是一個文件的ID,對應(yīng)的value是文件的位置。
Kademlia規(guī)范中描述的節(jié)點(diǎn)構(gòu)成。
節(jié)點(diǎn)通信
Kademlia節(jié)點(diǎn)發(fā)送和響應(yīng)有四種基本的遠(yuǎn)程過程調(diào)用。
PING
與互聯(lián)網(wǎng)控制消息協(xié)議(InternetControlMessageProtocol,ICMP)中的Ping非常相似,它是用于驗(yàn)證另一個節(jié)點(diǎn)是否仍處于活動狀態(tài)。
FINDNODE
發(fā)送此遠(yuǎn)程過程調(diào)用時要找到特定節(jié)點(diǎn)的ID。此遠(yuǎn)程過程調(diào)用的接收節(jié)點(diǎn)在自己的路由表中查找,并返回一組最接近正在查找的ID的聯(lián)系節(jié)點(diǎn)。
FINDVALUE
發(fā)送此遠(yuǎn)程過程調(diào)用時要帶有要定位的特定文件ID。如果接收節(jié)點(diǎn)在自己的分布式哈希表段中找到這個ID,它將返回響應(yīng)的Value(URL)。反之,則接收節(jié)點(diǎn)返回最接近文件ID的聯(lián)系節(jié)點(diǎn)列表。
STORE
此遠(yuǎn)程過程調(diào)用用于在接收節(jié)點(diǎn)的分布式哈希表段中存儲key/value對(file_id/location)。
每次成功完成遠(yuǎn)程過程調(diào)用后,發(fā)送節(jié)點(diǎn)和接收節(jié)點(diǎn)都會在各自的路由表中插入或更新彼此的聯(lián)系信息。
尋找對等點(diǎn)和文件
一個節(jié)點(diǎn)如何在Kademlia網(wǎng)絡(luò)中找到其他節(jié)點(diǎn)或者文件呢?我們可以用現(xiàn)實(shí)生活中的例子來舉例。
如果某個人想找到另一個不認(rèn)識的人,他可能會采取以下步驟:
他可以詢問離目標(biāo)人物更近的朋友。也許這些朋友和目標(biāo)人物在同一個行業(yè)工作,或者在同一個城市居住。
如果其中一個朋友知道目標(biāo)人物在哪里,那么就可以提供目標(biāo)人物的聯(lián)系信息,這樣查找就完成了。
如果這些朋友都不認(rèn)識目標(biāo)人物,他們可以給你提供可能認(rèn)識目標(biāo)人物的朋友的聯(lián)系信息。
然后你再詢問這些人,看看他們是否知道目標(biāo)人物,重復(fù)這一過程,直到找到目標(biāo)人物,或者達(dá)到某種停止查找的條件。
Kademlia節(jié)點(diǎn)在執(zhí)行查找時就遵循類似的模式。
如果一個節(jié)點(diǎn)要從網(wǎng)絡(luò)中檢索一條信息(一個文件),它將發(fā)送FINDVALUE的遠(yuǎn)程過程調(diào)用到它自己的聯(lián)系節(jié)點(diǎn)子集,這些聯(lián)系節(jié)點(diǎn)的ID與它要查找的文件的ID“最為接近”。如果任何接收節(jié)點(diǎn)在其分布式哈希表段中有這個ID,則它們將返回相應(yīng)的value,否則,它們將返回更接近所查詢的value的節(jié)點(diǎn)列表。
下一個要討論的問題是如何在Kademlia網(wǎng)絡(luò)中確定“距離”。
距離的計算
Kademlia將節(jié)點(diǎn)之間的距離定義為節(jié)點(diǎn)ID的“按位異或”(XOR)。XOR運(yùn)算比較兩個輸入值:如果這些輸入相同,則結(jié)果為false(0);如果輸入不同,則結(jié)果為true(1)。兩個數(shù)字的異或是通過在這兩個數(shù)字的二進(jìn)制表示中找到每一位的XOR來計算的。
例如,下圖假設(shè)4位密鑰空間中的節(jié)點(diǎn)ID為11(只有0~15的的ID是可能的)。為證明這個概念,我們使用一些其他ID來計算11的XOR。
在第一個例子中,我們計算節(jié)點(diǎn)ID11和節(jié)點(diǎn)ID10的XOR結(jié)果。兩個ID的前三位是相同的,只有最后一位不同。ID11和ID10進(jìn)行XOR運(yùn)算的結(jié)果是二進(jìn)制的0001,或者十進(jìn)制的1。
接下來我們計算ID11和ID12的XOR結(jié)果,只有第一位是相同的,而其他部分都是不同的。ID11和ID12的XOR運(yùn)算的結(jié)果是二進(jìn)制的0111,或十進(jìn)制的7。
我們最后的一個例子是計算ID11和ID4的XOR結(jié)果。這里所有的位都不相同,結(jié)果是二進(jìn)制的1111,十進(jìn)制為15。
從這些結(jié)果中,你會注意到Kademlia的XOR度量的一個重要特征:如果節(jié)點(diǎn)ID和當(dāng)前節(jié)點(diǎn)的ID的二進(jìn)制表示所共有的位相同個數(shù)越多,那么計算得到的XOR結(jié)果就越小。
Kademlia網(wǎng)絡(luò)和路由表
Kademlia中的每個節(jié)點(diǎn)都可以看做是二叉樹中的葉子。下面我們在4位密鑰空間中畫出所有可能的ID:0~15,從根(root)出發(fā),每一步往左則在該位上新增一個“0”,往右則在該位上新增一個“1”。
與Kademlia網(wǎng)絡(luò)相關(guān)的二叉樹最重要的特性是O(logn)查找時間。在Kademlia網(wǎng)絡(luò)中查找節(jié)點(diǎn)或文件是非常有效率的過程。
如前所述,路由表將聯(lián)系節(jié)點(diǎn)進(jìn)行分組并存儲到桶中,每個桶中包含一定距離的節(jié)點(diǎn)。這個距離就是“共享位長度”,它是通過節(jié)點(diǎn)ID與當(dāng)前ID進(jìn)行XOR運(yùn)算得到結(jié)果來得到的。
從下圖我們將看到這些桶是如何在一個4位密鑰空間中以ID11的節(jié)點(diǎn)中組織的。其ID與當(dāng)前節(jié)點(diǎn)共享前3位前綴的節(jié)點(diǎn)存儲在一個桶中,而ID與當(dāng)前節(jié)點(diǎn)共享前2位前綴的節(jié)點(diǎn)存儲在另一個桶中,以此類推。
文件切分和檢索
文件切分是將文件切分成更小的片段,命名為切片,并將files/names記錄在一個清單文件(即種子)中,這樣它們就可以按照適當(dāng)?shù)捻樞驒z索和重新合并。
文件切分提高了P2P網(wǎng)絡(luò)的分布性和可靠性,因?yàn)槎鄠€節(jié)點(diǎn)可以存儲共享文件的部分或全部切片。如果包含切片下載信息的節(jié)點(diǎn)離線了,此時可以從不同的源檢索該切片信息。
文件切分還可以節(jié)省網(wǎng)絡(luò)帶寬,共享潛在大文件的負(fù)載分布在許多節(jié)點(diǎn)中。
文件切分過程中會生成多個切片和一個清單文件(種子)。
在我們的實(shí)現(xiàn)中,文件添加和切分的過程是這樣的:
通過拖放將要共享的文件上傳到網(wǎng)絡(luò)中。
通過計算文件內(nèi)容的SHA1哈希值為文件創(chuàng)建ID。
這個ID被重新用作種子的文件名,擴(kuò)展名為“.xro”。這個過程對用戶來說是透明的,用戶只需知道ID即可。
原始文件切分成1MB塊,如果小于1MB,則切分為原始大小的50%。
每個切片都被寫入磁盤,切片的名稱就是其內(nèi)容的SHA1哈希值。
切片名稱是按照順序?qū)懭敕N子文件的,以及原始文件名和文件大小(以字節(jié)為單位)。
種子和切片位置信息通過STORE遠(yuǎn)程過程調(diào)用廣播給對等節(jié)點(diǎn)。對于每個shard/manifest,宿主節(jié)點(diǎn)在自己的路由表中查找與文件ID最接近的節(jié)點(diǎn)。這些對等節(jié)點(diǎn)都接收包含文件的ID和位置的STORE遠(yuǎn)程過程調(diào)用。
下面是JSON格式的種子內(nèi)容:
復(fù)制代碼
{
"file_name":"banana.mp4",
"length":9137395,
"pieces":[
"272610812651008498817059664145444816819140431736",
"255845820650928817902394043384061703021184974492",
"709124865584808529999187320247131501825035282844",
"463972141944555281071361859762050722622562309482",
"665233928362169136349271011451642022996948352498",
"460767108478119568061824765684889409150273585314",
"242856439500459087965632547950882773486858003109",
"1113118586291233368092664992853829437069513635744",
"55094692080869844054492088107211106202780121432"
]
}
文件檢索的處理方式也是類似的:
用戶輸入他們想要從網(wǎng)絡(luò)中檢索的文件的ID。這個ID是文件內(nèi)容的哈希值,也恰好是他們首先檢索的清單文件的名稱。
通過向最接近清單文件ID的對等節(jié)點(diǎn)發(fā)出FINDVALUE遠(yuǎn)程過程調(diào)用,從網(wǎng)絡(luò)中檢索清單文件。
下載清單文件后,節(jié)點(diǎn)為清單文件中記錄的每個切片重復(fù)查找和下載過程(FINDVALUR遠(yuǎn)程過程調(diào)用)。
下載完所有切片后,將它們重新合并到原始文件中。
然后,節(jié)點(diǎn)向最接近文件各自ID的對等節(jié)點(diǎn)廣播其獲取的切片和清單的位置信息。
從XorroP2P網(wǎng)絡(luò)中檢索文件的例子。
開發(fā)策略:從本地環(huán)境模擬并擴(kuò)展網(wǎng)絡(luò)應(yīng)用到真實(shí)網(wǎng)絡(luò)。
第一階段:測試模式
我們是如何著手構(gòu)建和測試的?
在項(xiàng)目的早期階段,我們需要測試節(jié)點(diǎn)間的通信,但我們只有類和測試套件,沒有網(wǎng)絡(luò),沒有遠(yuǎn)程過程調(diào)用傳輸,甚至也沒有幾臺計算機(jī)。
我們經(jīng)常啟動Ruby的交互式Shell(IRB),將幾個節(jié)點(diǎn)進(jìn)行實(shí)例化,并讓它們手動通信。當(dāng)然,這可以通過腳本來實(shí)現(xiàn)。但一旦引入真正的網(wǎng)絡(luò)環(huán)境,并且節(jié)點(diǎn)對象不能在相同的Ruby進(jìn)程中直接使用,否則它就會很快崩潰。
我們需要某種代理對象,它在測試和本地開發(fā)期間以一種方式運(yùn)行,在實(shí)際網(wǎng)絡(luò)環(huán)境中部署時又以另一種方式運(yùn)行。
我們的解決方案是讓每個節(jié)點(diǎn)將所有網(wǎng)絡(luò)通信都委托給先前存在的網(wǎng)卡(NetworkAdapter)對象。
在測試時,在這個網(wǎng)卡對象實(shí)際上是一個“FakeNetworkAdapter”(偽網(wǎng)卡),本質(zhì)上是一個由其他節(jié)點(diǎn)組成的數(shù)組,帶有一些用于查找和遠(yuǎn)程過程調(diào)用代理的方法。這允許我們在沒有遠(yuǎn)程過程調(diào)用傳輸協(xié)議的情況下在本地沙箱中測試節(jié)點(diǎn)的交互過程。
典型的工作流程如下圖所示:
節(jié)點(diǎn)A要求網(wǎng)絡(luò)向節(jié)點(diǎn)B發(fā)送遠(yuǎn)程過程調(diào)用,節(jié)點(diǎn)A提供聯(lián)系節(jié)點(diǎn)的ID、IP和端口。
網(wǎng)絡(luò)查找節(jié)點(diǎn)B是否存在于FakeNetwork(偽網(wǎng)絡(luò))環(huán)境中,這是通過聯(lián)系節(jié)點(diǎn)的ID來完成的。
網(wǎng)絡(luò)直接調(diào)用節(jié)點(diǎn)B上的方法,改變狀態(tài)和/或返回一些數(shù)據(jù)作為響應(yīng)。
網(wǎng)絡(luò)將該響應(yīng)傳遞回節(jié)點(diǎn)A,節(jié)點(diǎn)A反過來改變狀態(tài)或?qū)@些信息做出響應(yīng)。
第二階段:在本地沙箱中通過HTTP進(jìn)行遠(yuǎn)程過程調(diào)用
我們的下一步是引入HTTP協(xié)議作為構(gòu)建遠(yuǎn)程過程調(diào)用方法的基礎(chǔ)協(xié)議。
為此,我們實(shí)現(xiàn)了一個RealNetworkAdapter(真網(wǎng)卡)對象。它與我們的FakeNetworkAdapter具有相同的接口,但不是通過ID查找接收節(jié)點(diǎn)并直接調(diào)用該方法的,RealNetworkAdapter從所提供的聯(lián)系節(jié)點(diǎn)信息和對應(yīng)于遠(yuǎn)程過程調(diào)用的路由中列出的IP/端口處理一個HTTPPost請求,可能包括請求主體中的相關(guān)數(shù)據(jù)——請求節(jié)點(diǎn)的聯(lián)系信息、查詢信息等。
典型工作流程與上圖類似:
節(jié)點(diǎn)A要求網(wǎng)絡(luò)向節(jié)點(diǎn)B提供遠(yuǎn)程過程調(diào)用,節(jié)點(diǎn)A提供聯(lián)系節(jié)點(diǎn)的ID、IP和端口。
網(wǎng)絡(luò)為IP、端口和遠(yuǎn)程過程調(diào)用路徑生成HTTPPOST請求,并將其與任何有效負(fù)載數(shù)據(jù)一起發(fā)送。
接收節(jié)點(diǎn)的HTTP端點(diǎn)接收POST請求,并調(diào)用Node對象上的接收遠(yuǎn)程過程調(diào)用的方法。
此接收遠(yuǎn)程過程調(diào)用方法的返回值通過HTTP端點(diǎn)轉(zhuǎn)換為HTTP響應(yīng),并發(fā)送回調(diào)用節(jié)點(diǎn)的網(wǎng)卡。
網(wǎng)絡(luò)接收響應(yīng),并將其傳遞給節(jié)點(diǎn)A,節(jié)點(diǎn)A反過來更改狀態(tài)或?qū)@些新信息做出響應(yīng)。
正如你所見到的,從節(jié)點(diǎn)的角度來看,并沒有什么變化:每個節(jié)點(diǎn)都發(fā)送和接收相同的信息,但是網(wǎng)絡(luò)對象和HTTP端點(diǎn)抽象出節(jié)點(diǎn)之間的通信。
第三階段:RPC/HTTP在互聯(lián)網(wǎng)環(huán)境下傳遞
一旦我們知道節(jié)點(diǎn)間的通信使用HTTP協(xié)議,我們的下一步就是將節(jié)點(diǎn)部署到互聯(lián)網(wǎng)上的多個系統(tǒng)上。如果系統(tǒng)直接在公共互聯(lián)網(wǎng)上運(yùn)行,或者它們位于已配置打開端口的防火墻之后,這種方法就可以很好地工作。
NAT防火墻后的節(jié)點(diǎn)則是另一回事。它們可以很好地加入網(wǎng)絡(luò)并檢索文件,但如果沒有端口轉(zhuǎn)發(fā)的話,它們就不能接收傳入的HTTP連接,因此不能對網(wǎng)絡(luò)做出貢獻(xiàn)。
從長遠(yuǎn)來看,目標(biāo)是基于TCP/UDP協(xié)議的遠(yuǎn)程過程調(diào)用和文件傳輸協(xié)議,同時支持STUN和TURN服務(wù)器來處理公共IP/端口發(fā)現(xiàn)和傳入連接。但是,在這個項(xiàng)目中,我們需要一種快速繞過NAT和防火墻的方法。
為此,我們構(gòu)建了對Ngrok的支持,這是一個第三方隧道服務(wù),這樣,防火墻后面的節(jié)點(diǎn)就可以成為我們網(wǎng)絡(luò)中完全正常運(yùn)行、功能齊全的成員。
靈活的節(jié)點(diǎn)實(shí)例化和啟動腳本
我們意識到現(xiàn)在有許多不同的節(jié)點(diǎn)配置/環(huán)境需要支持和測試。此外,我們還需要一種能夠在這些配置之間快速、無縫切換的方法。
在每個環(huán)境中,一個節(jié)點(diǎn)廣播它的IP/端口,以及它所托管的每個切片或種子的完整URL/路徑。
如果我們只在本地環(huán)境中工作的話,那么所有節(jié)點(diǎn)都在不同的TCP端口從“本地”進(jìn)行廣播。
如果節(jié)點(diǎn)直接在互聯(lián)網(wǎng)上,它應(yīng)該廣播自己的公共IP和端口。
如果節(jié)點(diǎn)具有完全限定的域名,那么該節(jié)點(diǎn)應(yīng)該廣播該域名,而不是數(shù)字IP地址。
如果節(jié)點(diǎn)位于防火墻之后,且防火墻已正確配置用于NAT轉(zhuǎn)換/端口轉(zhuǎn)發(fā),則該節(jié)點(diǎn)仍然可以依賴它的公共IP和端口。
如果節(jié)點(diǎn)位于沒有轉(zhuǎn)發(fā)端口的防火墻后面,則需要:
建立到Ngrok服務(wù)器的隧道,并注意到它唯一的Ngrok地址,這樣它就可以將地址廣播給其他節(jié)點(diǎn)。
要知道它是一個“Leech”節(jié)點(diǎn),而不是廣播它所托管的任何文件的位置信息。
我們確定了節(jié)點(diǎn)可能擁有的許多配置,并創(chuàng)建了環(huán)境變量來表示這些選項(xiàng)。我們的節(jié)點(diǎn)實(shí)例化代碼對這些變量做出反應(yīng),我們設(shè)計了一系列Bash腳本,來為我們工作的每個環(huán)境標(biāo)準(zhǔn)化這些配置:測試、本地、局域網(wǎng)、廣域網(wǎng)等。
系統(tǒng)架構(gòu)
我們最終的系統(tǒng)架構(gòu)如下所示:
頂級對象是XorroNode,它是一個模塊化的Sinatra應(yīng)用程序。
每個XorroNode包含:
一個單獨(dú)的Kademlia節(jié)點(diǎn),負(fù)責(zé)維護(hù)自己的路由表和分布式哈希表。
用于向其他節(jié)點(diǎn)發(fā)起遠(yuǎn)程過程調(diào)用的網(wǎng)卡。
用于從其他節(jié)點(diǎn)、WebUI和文件傳輸接收遠(yuǎn)程過程調(diào)用的Sinatra端點(diǎn)。
用于文件接收、切分和清單文件的創(chuàng)建。謝謝