Java 进程挂在 IOUtils 上.疑似死锁



我有一个java进程挂起在调用IOUtils.toString中,使用以下代码:

String html = "";
try {
    html = IOUtils.toString(someUrl.openStream(), "utf-8"); // process hangs on this line
} catch (Exception e) {
    return null;
}

它无法可靠地重现这一点。它是网络爬虫的一部分,因此成功执行此行数千次,但最终导致该过程在几天后挂起。

jstack的输出:

2013-09-25 09:09:36
Full thread dump OpenJDK 64-Bit Server VM (20.0-b12 mixed mode):
"Attach Listener" daemon prio=10 tid=0x00007f2b1c001000 nid=0x225a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
"Thread-0" prio=10 tid=0x00007f2b34122000 nid=0x187b runnable [0x00007f2b30970000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(SocketInputStream.java:146)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:275)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
        - locked <0x00000000e3d2d160> (a java.io.BufferedInputStream)
        at sun.net.www.http.ChunkedInputStream.readAheadBlocking(ChunkedInputStream.java:552)
        at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:609)
        at sun.net.www.http.ChunkedInputStream.read(ChunkedInputStream.java:696)
        - locked <0x00000000e3d30558> (a sun.net.www.http.ChunkedInputStream)
        at java.io.FilterInputStream.read(FilterInputStream.java:133)
        at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:2582)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:282)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:324)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:176)
        - locked <0x00000000e3d317d0> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.Reader.read(Reader.java:140)
        at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1364)
        at org.apache.commons.io.IOUtils.copy(IOUtils.java:1340)
        at org.apache.commons.io.IOUtils.copy(IOUtils.java:1315)
        at org.apache.commons.io.IOUtils.toString(IOUtils.java:525)

我看不到任何在 toString 方法上设置超时的方法。有什么建议吗?这是 Apache 共享资源中的错误吗?或者在我的OpenJDK中?

你对 toString() 的调用最终会转发到 copyLarge()。在这里,您可以看到从流中读取将继续,直到 InputStream.read() 检测到文件结束 (EOF) 标记。根据这篇文章,read() 可以读取 0 个字节,即,如果您的读取的 URLConnection 没有返回 EOF 标记,该方法可能会永远读取 0 个字节。

也许您可以追踪导致问题的 URL?

无论如何,要实现超时,您可以在单独的线程中开始每个读取,并在经过一定时间后终止该线程。

我决定尝试简单地使用 guava IO,因为它已经在我的类路径中:

String html = "";
try {
    InputSupplier<? extends InputStream> supplier = Resources
            .newInputStreamSupplier(metaUrl);
    html = CharStreams.toString(CharStreams.newReaderSupplier(supplier,
            Charsets.UTF_8));
} catch (Exception e) {
    return null;
}

崩溃通常需要几天时间,所以如果我几天内没有更新这个答案,假设这有效!

更新:到目前为止7天没有挂起... :)

我遇到了同样的问题。也许通过使用番石榴可以解决,但在我看来,问题的根源是套接字没有配置 soTimeout。

尝试

socket.setSoTimeout(10000)

在 10 秒后没有 EOF 时抛出 SocketTimeoutException。

Java 原生方法:

InputStream in = new URL(url).openStream();

番石榴法:

输入供应商供应商 = Resources.newInputStreamSupplier(new URL(url)); InputStream in = supplier.getInput();

它们都将抛出连接超时异常。因为guave也使用URL.openStream()

但是有些网站太慢了,以至于我每次都能从中读取一点数据,而且很多很多次仍然没有到达终点。我也看到它被Jstack挂在那里。

像这样(也许只是在我的主机上很慢):一个 txt 文件地址

相关内容

  • 没有找到相关文章

最新更新