许多Java线程从web下载内容,但不会使带宽饱和
不久前,我尝试用Java实现一个爬虫程序,并离开了该项目一段时间(此后取得了很大进展)。基本上,我已经实现了一个大约200-400个线程的爬虫程序,每个线程连接并下载一个页面的内容(为了清晰起见,进行了简化,但基本上就是这样): 这很有效。问题是我只使用了互联网带宽的一小部分。由于能够以>6MB/s的速度下载,我已经确定(使用NetLimitor和我自己的计算)在下载页面源时最多只能使用大约1MB/s 我已经做了大量的统计和分析,这是合理的-如果计算机不能有效地支持超过400个线程(我也不知道,但更多的线程似乎无效),并且每个连接大约需要4秒钟才能完成,然后我应该每秒下载100页,这就是实际发生的情况。奇怪的是,很多时候当我运行这个程序时,互联网连接被完全阻塞了-我和我的wifi连接上的任何人都不能正常访问网络(当我只使用16%时!下载其他文件,比如电影时,不会发生这种情况) 在来到这里之前,我花了数周时间计算、分析和收集各种统计数据(确保所有线程都使用VM监视器运行,计算线程的平均运行时间,excel图表…),但我已经没有答案了。我想知道这种行为是否可以解释。我意识到这个问题中有很多“如果”,但这是我能做的最好的事情,而不是把它变成一篇文章 我的电脑规格为i5 4460,配备8GB DDR3-1600和100Mb/s(有效约8MB/s)互联网连接,通过局域网直接连接到爬虫。我在寻找大致的方向——我还应该去哪里 (我指的是对有经验的开发人员而不是我自己清楚的令人讨厌的东西)为了:许多Java线程从web下载内容,但不会使带宽饱和,java,multithreading,web-crawler,jsoup,router,Java,Multithreading,Web Crawler,Jsoup,Router,不久前,我尝试用Java实现一个爬虫程序,并离开了该项目一段时间(此后取得了很大进展)。基本上,我已经实现了一个大约200-400个线程的爬虫程序,每个线程连接并下载一个页面的内容(为了清晰起见,进行了简化,但基本上就是这样): 这很有效。问题是我只使用了互联网带宽的一小部分。由于能够以>6MB/s的速度下载,我已经确定(使用NetLimitor和我自己的计算)在下载页面源时最多只能使用大约1MB/s 我已经做了大量的统计和分析,这是合理的-如果计算机不能有效地支持超过400个线程(我也不知道,
Amir.问题不在于DNS解析,因为使用IP地址创建连接(我预先存储了所有地址,然后使用了这些地址)会导致完全相同的响应时间和带宽使用。这也不是问题所在 我现在怀疑是NetLimitor程序的“错误”。我已经直接测量了接收到的字节数并将其输出到磁盘(我以前做过这项工作,但很明显我在程序中做了一些更改)。看来我的带宽已经饱和了。此外,当切换到HttpUrlConnection对象而不是Jsoup时,netlimiter程序确实显示出更大的带宽使用率。也许它与Jsoup有一些问题。
我不确定这是否是问题所在,但根据经验,该程序下载了大量数据。因此,我希望这对将来可能遇到类似问题的人有所帮助。我可能会有一个单独的线程池用于下载,另一个用于解析。解析是受CPU限制的,因此拥有比内核更多的解析器线程可能会降低速度,而您可能希望动态调整下载池的大小,下载池通常比解析器池大得多。这不一定能解决你的问题,但它使调整变得容易得多。我会把责任推到路由器上,可能也推到你的提供商身上。这种程序在(虚拟)服务器宿主的服务器上运行得更好。主机与Internet的连接不同(更快、更简单)。此外,如果您正在下载许多小页面,那么HTTP窃听器可能会使您的实际带宽使用量增加一倍或三倍。@petermm也有可能当连接数达到饱和时,您无法再进行DNS查找,或者至少我在一些路由器上也看到了这一点。嗯,400个线程确实不是最好的主意。一种现代方法是使用输入多路复用。这里有一个
// we're in a run() method of a truely generic Runnable.
// _url is a member passed to the Runnable object beforehand.
Connection c = Jsoup.connect(_url).timeout(10000);
c.execute();
Document d = c.response().parse();
// use Jsoup to get the links, add them to the backbone of the crawler
// to be checked and maybe later passed to the crawling queue.