【前五篇】系列文章传送门: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 网络,会经过相互通讯,维护本身朋友圈好友的状态。
经过上面这个机制,保证了任意节点的加入和离开都不影响总体网络。
参考: