【前五篇】系列文章傳送門:html
「兄弟,有種子嗎?」
「什麼種子?小麥種嗎?」
「......,來,哥今天帶你認識下什麼是種子」。算法
你們提及種子,應該都知道是用來下載資源的。那麼資源下載都有哪些方式?種子下載又有什麼優點呢?數據庫
第一種是經過 HTTP 進行下載。這種方式,有過經歷的人應該體會到,當下載文件稍大點,下載速度簡直能把人急死。編程
第二種方式就是是經過 FTP(文件傳輸協議)。FTP 採用兩個 TCP 鏈接來傳輸一個文件。服務器
在 FTP 的兩個 TCP 鏈接中,每傳輸一個文件,都要新創建一個數據鏈接。基於這個數據鏈接,FTP 又有兩種工做模式:主動模式(PORT)和被動模式(PASV),要注意的是,這裏的主動和被動都是站在服務器角度來講的。工做模式過程以下:微信
主動模式工做流程網絡
被動模式工做流程分佈式
上面說了 HTTP 下載和 FTP 下載,這兩種方式都有一個大缺點-難以解決單一服務器的帶寬壓力。由於它們使用的都是傳統 C/S 結構,這種結構會隨着客戶端的增多,下載速度愈來愈慢。這在當今互聯網世界顯然是不合理的,咱們指望能實現「下載人數越多,下載速度不變甚至更快」的願望。工具
後來,一種創新的,稱爲 P2P 的方式實現了咱們的願望。測試
P2P 就是 peer-to-peer。這種方式的特色是,資源一開始並不集中存儲在某些設備上,而是分散地存儲在多臺設備上,這些設備咱們稱爲 peer。
在下載一個文件時,只要獲得那些已經存在了文件的 peer 地址,並和這些 peer 創建點對點的鏈接,就能夠就近下載文件,而不須要到中心服務器上。一旦下載了文件,你的設備也就稱爲這個網絡的一個 peer,你旁邊的那些機器也可能會選擇從你這裏下載文件。
經過這種方式解決上面 C/S 結構單一服務器帶寬壓力問題。若是使用過 P2P2 軟件,例如 BitTorrent,你就會看到本身網絡不只有下載流量,還有上傳流量,也就是說你加入了這個 P2P 網絡,本身能夠從這個網絡裏下載,同時別人也能夠從你這裏下載。這樣就實現了,下載人數越多,下載速度越快的願望。
上面整個過程是否是很完美?是的,結果很美好,但爲了實現這個美好,咱們仍是有不少準備工做要作的。好比,咱們怎麼知道哪些 peer 有某個文件呢?
這就用到咱們常說的種子(.torrent)。 .torrent 文件由Announce(Tracker URL)和文件信息兩部分組成。
其中,文件信息裏有如下內容:
下載時,BT 客戶端首先解析 .torrent 文件,獲得 Tracker 地址,而後鏈接 Tracker 服務器。Tracker 服務器迴應下載者的請求,將其餘下載者(包括髮布者)的 IP 提供給下載者。
下載者再鏈接其餘下載者,根據 .torrent 文件,二者分別對方本身已經有的塊,而後交換對方沒有的數據。
能夠看到,下載的過程不須要其餘服務器參與,並分散了單個線路上的數據流量,減輕了服務器的壓力。
下載者每獲得一個塊,須要算出下載塊的 Hash 驗證碼,並與 .torrent 文件中的進行對比。若是同樣,說明塊正確,不同就須要從新下載這個塊。這種規定是爲了解決下載內容的準確性問題。
從這個過程也能夠看出,這種方式特別依賴 Tracker。Tracker 須要收集全部 peer 的信息,並將從信息提供給下載者,使下載者相互鏈接,傳輸數據。雖然下載的過程是非中心化的,可是加入這個 P2P 網絡時,須要藉助 Tracker 中心服務器,這個服務器用來登記有哪些用戶在請求哪些資源。
因此,這種工做方式有一個弊端,一旦 Tracker 服務器出現故障或者線路被屏蔽,BT 工具就沒法正常工做了。那能不能完全去中心化呢?答案是能夠的。
DHT(Distributed Hash Table),這個網絡中,每一個加入 DHT 網絡的人,都要負責存儲這個網絡裏的資源信息和其餘成員的聯繫信息,至關於全部人一塊兒構成了一個龐大的分佈式存儲數據庫。
而 Kedemlia 協議 就是一種著名的 DHT 協議。咱們來基於這個協議來認識下這個神奇的 DHT 網絡。
當一個客戶端啓動 BitTorrent 準備下載資源時,這個客戶端就充當了兩個角色:
在 DHT 網絡裏面,每個 DHT Node 都有一個 ID。這個 ID 是一個長字符串。每一個 DHT Node 都有責任掌握一些「知識」,也就是文件索引。也就是說,每一個節點要知道哪些文件是保存哪些節點上的。注意,這裏它只須要有這些「知識」就能夠了,而它自己不必定就是保存這個文件的節點。
固然,每一個 DHT Node 不會有全局的「知識」,也就是說它不知道全部的文件保存位置,只須要知道一部分。這裏的一部分,就是經過哈希算法計算出來的。
每一個文件能夠計算出一個哈希值,而 DHT Node 的 ID 是和哈希值相同長度的串。
對於文件下載,DHT 算法是這樣規定的:
若是一個文件計算出一個哈希值,則和這個哈希值同樣的那個 DHT Node,就有責任知道從哪裏下載這個文件,即使它本身沒保存這個文件。
固然不必定總這麼巧,都能找到和哈希值如出一轍的,有可能文件對應的 DHT Node 下線了,因此 DHT 算法還規定:
除了如出一轍的那個 DHT Node 應該知道文件的保存位置,ID 和這個哈希值很是接近的 N 個 DHT Node 也應該知道。
以上圖爲例。文件 1 經過哈希運算,獲得匹配 ID 的 DHT Node 爲 Node C(固然還會有其餘的,爲了便於理解,我們就先關注 Node C),因此,Node C 就有責任知道文件 1 的存放地址,雖然 Node C 自己沒有存放文件 1。
同理,文件 2 經過哈希計算,獲得匹配 ID 的 DHT Node 爲 Node E,可是 Node D 和 E 的值很近,因此 Node D 也知道。固然,文件 2 自己不必定在 Node D 和 E 這裏,可是咱們假設 E 就有一份。
接下來,一個新節點 Node new 上線了,若是要下載文件 1,它首先要加入 DHT 網絡。如何加入呢?
在這種模式下,種子 .torrent 文件裏面就再也不是 Tracker 的地址了,而是一個 list 的 Node 地址,全部這些 Node 都是已經在 DHT 網絡裏面的。固然,隨着時間的推移,頗有可能有退出的,有下線的,這裏咱們假設,不會全部的都聯繫不上,總有一個能聯繫上。
那麼,Node new 只要在種子裏面找到一個 DHT Node,就加入了網絡。
Node new 不知道怎麼聯繫上 Node C,由於種子裏面的 Node 列表裏面極可能沒有 Node C,可是不要緊,它能夠問。DHT 網絡特別像一個社交網絡,Node new 會去它能聯繫上的 Node 問,大家知道 Node C 的聯繫方式嗎?
在 DHT 網絡中,每一個 Node 都保存了必定的聯繫方式,可是確定沒有全部 Node 的聯繫方式。節點之間經過相互通訊,會交流聯繫方式,也會刪除聯繫方式。這和人們的溝通方式同樣,你有你的朋友圈,他有他的朋友圈,大家互相加微信,就互相認識了,可是過一段時間不聯繫,就可能會刪除朋友關係同樣。
在社交網絡中,還有個著名的六度理論,就是說社交網絡中的任何兩我的的直接距離不超過六度,也就是即便你想聯繫比爾蓋茨,最多經過六我的就可以聯繫上。
因此,Node New 想聯繫 Node C,就去萬能的朋友圈去問,而且求轉發,朋友再問朋友,直到找到 C。若是最後找不到 C,可是能找到離 C 很近的節點,也能夠經過 C 的相鄰節點下載文件 1。
在 Node C上,告訴 Node new,要下載文件 1,能夠去 B、D、F,這裏咱們假設 Node new 選擇了 Node B,那麼新節點就和 B 進行 peer 鏈接,開始下載。它一旦開始下載,本身本地也有文件 1 了,因而,Node new 就告訴 C 以及 C 的相鄰節點,我也有文件 1 了,能夠將我加入文件 1 的擁有者列表了。
你可能會發現,上面的過程當中漏掉了 Node new 的文件索引,可是根據哈希算法,必定會有某些文件的哈希值是和 Node new 的 ID 匹配的。在 DHT 網絡中,會有節點告訴它,你既然加入了我們這個網絡,也就有責任知道某些文件的下載地址了。
好了,完成分佈式下載了。可是咱們上面的過程當中遺留了兩個細節性的問題。
1)DHT Node ID 以及文件哈希值是什麼?
其實,咱們能夠將節點 ID 理解爲一個 160bits(20字節)的字符串,文件的哈希也使用這樣的字符串。
2)所謂 ID 類似,具體到什麼程度算類似?
這裏就要說到兩個節點距離的定義和計算了。
在 Kademlia 網絡中,兩個節點的距離採用的是邏輯上的距離,假設節點 A 和 節點 B 的距離爲 d,則:
d = A XOR B
上面說過,每一個節點都有一個哈希 ID,這個 ID 由 20 個字符,160 bits 位組成。這裏,咱們就用一個 5 bits ID 來舉例。
咱們假設,節點 A 的 ID 是 01010,節點 B 的 ID 是 01001,則:
距離 d = A XOR B = 01010 XOR 00011 = 01001 = 9
因此,咱們說節點 A 和節點 B 的邏輯距離爲 9。
回到咱們上面的問題,哈希值接近,能夠理解爲距離接近,也即,和這個節點距離近的 N 個節點要知道文件的保存位置。
要注意的是,這個距離不是地理位置,由於在 Kademlia 網絡中,位置近不算近,ID 近纔算近。咱們能夠將這個距離理解爲社交距離,也就是在朋友圈中的距離,或者社交網絡中的距離。這個和你的空間位置沒有多少關係,和人的經歷關係比較大。
就像人同樣,雖然咱們常聯繫的只有少數,可是朋友圈確定是遠近都有。DHT 網絡的朋友圈也同樣,遠近都有,而且按距離分層。
假設某個節點的 ID 爲 01010,若是一個節點的 ID,前面全部位數都與它相同,只有最後 1 位不停,這樣的節點只有 1 個,爲 01011。與基礎節點的異或值爲 00001,也就是距離爲 1。那麼對於 01010 而言,這樣的節點歸爲第一層節點,也就是k-buket 1。
相似的,若是一個節點的 ID,前面全部位數和基礎節點都相同,從倒數第 2 位開始不一樣,這樣的節點只有 2 個,即 01000 和 01001,與基礎節點的亦或值爲 00010 和 00011,也就是距離爲 2 和 3。這樣的節點歸爲第二層節點,也就是k-bucket 2。
因此,咱們能夠總結出如下規律:
若是一個節點的 ID,前面全部位數相同,從倒數第 i 位開始不一樣,這樣的節點只有 2^(i-1) 個,與基礎節點的距離範圍爲 [2^(i-1), 2^i],對於原始節點而言,這樣的節點歸爲 k-bucket i。
你會發現,差距越大,陌生人就越多。可是朋友圈不能把全部的都放下,因此每一層都只放 K 個,這個 K 是能夠經過參數配置的。
假設,Node A 的 ID 爲 00110,要找 B(10000),異或距離爲 10110,距離範圍在 [2^4, 2^5),這就說明 B 的 ID 和 A 的從第 5 位開始不一樣,因此 B 可能在 k-bucket 5 中。
而後,A 看看本身的 k-bucket 5 有沒有 B,若是有,結束查找。若是沒有,就在 k-bucket 5 裏隨便找一個 C。由於是二進制,C、B 都和 A 的第 5 位不停,那麼 C 的 ID 第5 位確定與 B 相同,即它與 B 的距離小於 2^4,至關於 A、B 之間的距離縮短了一半以上。
接着,再請求 C,在 C 的通信裏裏,按一樣的查找方式找 B,若是 C 找到了 B,就告訴 A。若是 C 也沒有找到 B,就按一樣的搜索方法,在本身的通信裏裏找到一個離 B 更近一步的 D(D、B 之間距離小於 2^3),把 D 推薦給 A,A 請求 D 進行下一步查找。
你可能已經發現了,Kademlia 這種查詢機制,是經過折半查找的方式來收縮範圍,對於總的節點數目爲 N 的網絡,最多隻須要 log2(N) 次查詢,就可以找到目標。
以下圖,A 節點找 B 節點,最壞查找狀況:
圖中過程以下:
在 Kademlia 算法中,每一個節點下面 4 個指令:
整個 DHT 網絡,會經過相互通訊,維護本身朋友圈好友的狀態。
經過上面這個機制,保證了任意節點的加入和離開都不影響總體網絡。
參考: