

新闻资讯
哈希游戏| 哈希游戏平台| 哈希游戏APP哈希游戏- 哈希游戏平台- 哈希游戏官方网站
DHT网络本质上是一个用于查询的网络,其用于查询一个资源有哪些计算机正在下载。每个资源都有一个20字节长度的ID用于标示,称为infohash。当一个程序作为DHT节点加入这个网络时,就会有其他节点来向你查询,当你做出回应后,对方就会记录下你。对方还会询问其他节点,当对方开始下载这个infohash对应的资源时,他就会告诉所有曾经询问过的节点,包括你。这个时候就可以确定,这个infohash对应的资源在这个网络中是有效的。
获取到infohash后能做什么?关键点在于,我们现在使用的磁力链接(magnet url),是和infohash对应起来的。也就是拿到infohash,就等于拿到一个磁力链接。但是这个爬虫还需要建立资源的信息,这些信息来源于种子文件。种子文件其实也是对应到一个资源,种子文件包含资源名、描述、文件列表、文件大小等信息。获取到infohash时,其实也获取到了对应的计算机地址,我们可以在这些计算机上下载到对应的种子文件。
但是,以一个节点的身份加入DHT网络,是无法获取大量查询的。在DHT网络中,每个节点都有一个ID。每个节点在查询信息时,仅询问离信息较近的节点。这里的信息除了infohash外还包含节点,即节点询问一个节点,这个节点在哪里。DHT的典型实现中(Kademlia),使用两个ID的xor操作来确定距离。既然距离的计算是基于ID的,为了尽可能获取整个DHT网络交换的信息,爬虫程序就可以建立尽可能多的DHT节点,让这些节点的ID均匀地分布在ID取值区间内,以这样的方式加入网络。
除了尽可能多地往DHT网络里部署节点之外,对单个节点而言,也有些注意事项。例如应尽可能快地将自己告诉尽可能多的节点,这可以在启动时进行大量的随机infohash的查询。随着查询过程的深入,该节点会与更多的节点打交道。因为DHT网络里的节点实际上是不稳定的,它今天在线,明天后天可能不在线,所以计算你的ID固定,哪些节点与你较近,本身就是个相对概念。节点在程序退出时,也最好将自己的路由信息(与自己交互的节点列表)保存起来,这样下次启动时就可以更快地加入网络。
DHT节点的完整实现是比较繁琐的,涉及到查询以及繁杂的各种对象的超时(节点、桶、infohash),而超时的处理并不是粗暴地做删除操作。因为本身是基于UDP协议,你得对这些超时对象做进一步的查询才能正确地进一步做其他事情。而搜索也是个繁杂的事情,递归地查询节点,感觉上,你不一定离目标越来越近,由于被查询节点的不确定性(无法确定对方是否在玩弄你,或者本身对方就是个),你很可能接下来要查询的节点反而离目标变远了。
couchdb看起来不支持字符串查询,我得自己创建一个view,这个view里我通过翻阅了一些资料写了一个将每个doc的name拆分成若干次查询结果的map。这个map在处理每一次查询时,我都得动态更新之。couchdb是不支持局部更新的,这还不算大问题。然后很高兴,终于支持字符串查询了。这里的字符串查询都是基于字符串的子串查询。但是问题在于,太慢了。每一次在WEB端的查询,都直接导致erlang进程的call超时。
我可以搜索hello,基于单词。mongodb会自动拆词。更关键更让人爽的是,要开启这个功能非常简单:设置启动参数、建立索引。没了。mongodb的erlang客户端库mongodb-erlang也只依赖一个bson-erlang库。然后我又埋头苦干,几个小时候我的这个爬虫程序就可以在浏览器端搜索关键字了。后来我发现,mongodb的全文搜索是不支持中文的。因为它还不知道中文该怎么拆词。恰好我有个同事做过中文拆词的研究,看起来涉及到很复杂的算法。直到这个时候,我他妈才醒悟,我为什么需要基于单词的搜索。我们大部分的搜索其实都是基于子字符串的搜索。
都说可以用erlang来编写高容错的服务器程序。看看它的supervisor,监视子进程,自动重启子进程。天生的容错功能,就算你宕个几次,单个进程自动重启,整个程序看起来还稳健地在运行,多牛逼啊。再看看erlang的进程,轻量级的语言特性,就像OOP语言里的一个对象一样轻量。如果说使用OOP语言写程序得think in object,那用erlang你就得think in process,多牛逼多骇人啊。
在erlang中,对于一些异步操作,你可以通过进程间的交互将这个操作包装成同步接口,例如ping的实现,可以等到对方回应之后再返回。被阻塞的进程反正很轻量,其包含的逻辑很单一。这不但是一种良好的包装,甚至可以说是一种erlang-style。但这很容易带来死锁。在最开始的时候我没有注意这个问题,当爬虫节点数上升的时候,网络数据复杂的时候,似乎就出现了死锁型宕机(进程互相等待太久,直接timeout)。