10

我正在使用 htmlunit 开发一个网络爬虫,我已经添加了所有必需的超时,但是我注意到当我使用 Java VisualVM 进行线程转储时,当某个网站的服务器被爬网时应用程序挂起:

java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at java.net.SocksSocketImpl.readSocksReply(SocksSocketImpl.java:88)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:429)
at java.net.Socket.connect(Socket.java:525)
at com.gargoylesoftware.htmlunit.SocksSocketFactory.connectSocket(SocksSocketFactory.java:89)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148)
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:776)
at com.gargoylesoftware.htmlunit.HttpWebConnection.getResponse(HttpWebConnection.java:152)
at app.plugin.core.net.QHttpWebConnection.getResponse(QHttpWebConnection.java:30)
at com.gargoylesoftware.htmlunit.WebClient.loadWebResponseFromWebConnection(WebClient.java:1439)
at com.gargoylesoftware.htmlunit.WebClient.loadWebResponse(WebClient.java:1358)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:307)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:373)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:358)

这真的很令人沮丧,因为我无法控制这些服务器。这个问题严重影响了我的应用程序的性能。

问题:

  1. 我该如何解决这个问题?
  2. 有没有办法获取Java应用程序打开的套接字连接列表并使用它来终止套接字,例如模拟服务器关闭连接?
4

3 回答 3

11

我相信,当您使用 Java 本机方法时,即使调用实际上被阻塞等待某个事件,堆栈跟踪也会说 RUNNABLE。从本质上讲,我不相信 Java 有任何方法知道本地方法实际上在做什么,因此它将这些调用标记为 RUNNABLE。我已经在 socketRead0() 和 socketAccept() 中看到了这一点——它们通常都会阻塞。

您需要将超时设置为合理的时间长度,以便在服务器没有响应时您的请求将超时,但在服务器很忙的情况下也不会太短。您的应用程序应该被编写为使用多个线程。我会尝试运行十几个或更多线程,并让每个线程等待长达五到十秒的响应。让少数线程等待几乎没有开销。在编写网络爬虫时,您还应该注意不要用大量请求轰炸服务器。

于 2012-09-22T17:50:22.473 回答
6

这是一篇可能相关的博客文章:http: //javaeesupportpatterns.blogspot.fi/2011/04/javanetsocketinputstreamsocketread0.html

简而言之,解决方案是确保定义了套接字超时。默认值为 0,表示没有超时。究竟如何,这取决于图书馆,在这种情况下显然是com.gargoylesoftware.htmlunit. 快速浏览一下正确的方法可能com.gargoylesoftware.htmlunit.WebClient.setTimeout

于 2013-03-25T09:59:05.347 回答
1

如果您的 Java 服务器在 Windows 上,您最后的选择是 SysInternals TCPView。

http://technet.microsoft.com/en-us/sysinternals/bb897437.aspx

从中您将看到所有进程以及所有本地和远程端口的列表,其中将包括您的 Java 应用程序。您必须选择正确的连接来关闭,然后,Java 线程将抛出异常并结束。

当然存在关闭错误连接的风险。毕竟,这种方法是最后的手段。

2019 年 8 月 23 日更新:

当有大量连接时,TCPView 很慢。

更快的替代方案是 CurrPorts(来自 NirSoft):https ://www.nirsoft.net/utils/cports.html

于 2014-03-25T03:31:43.133 回答