Java的HttpURLConnection的InputStream.close()挂起(或工作时间

2019-08-07 04:39发布

首先,一些背景。 有哪些扩展/解决一堆短网址的工人:

http://t.co/example -> http://example.com

所以,我们只要按照重定向。 而已。 我们并没有从连接任何数据。 我们得到了200之后,我们返回最终网址和密切的InputStream。

现在,这个问题本身。 在生产服务器上的解析器线程之一挂内InputStream.close()调用:

"ProcessShortUrlTask" prio=10 tid=0x00007f8810119000 nid=0x402b runnable [0x00007f882b044000]
   java.lang.Thread.State: RUNNABLE
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
        at java.io.BufferedInputStream.skip(BufferedInputStream.java:352)
        - locked <0x0000000561293aa0> (a java.io.BufferedInputStream)
        at sun.net.www.MeteredStream.skip(MeteredStream.java:134)
        - locked <0x0000000561293a70> (a sun.net.www.http.KeepAliveStream)
        at sun.net.www.http.KeepAliveStream.close(KeepAliveStream.java:76)
        at java.io.FilterInputStream.close(FilterInputStream.java:155)
        at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.close(HttpURLConnection.java:2735)
        at ru.twitter.times.http.URLProcessor.resolve(URLProcessor.java:131)
        at ru.twitter.times.http.URLProcessor.resolve(URLProcessor.java:55)
        at ...

短暂的研究之后,我明白了skip()被调用,以清理其发送回连接池前的数据流(如果保持活动设置上?)。 不过我不知道如何避免这种情况。 此外,如果有一些不好的设计在我们的代码还是有问题JDK我对此表示怀疑。

所以,问题是:

  1. 是否有可能避免挂在close() ? 保证一些合理的超时,例如。
  2. 是否有可能避免在所有连接读取数据? 还记得我只是想最后的URL。 其实我觉得,我不想skip()在所有被称为...

更新:

KeepAliveStream ,线79, close()方法:

    // Skip past the data that's left in the Inputstream because
    // some sort of error may have occurred.
    // Do this ONLY if the skip won't block. The stream may have
    // been closed at the beginning of a big file and we don't want
    // to hang around for nothing. So if we can't skip without blocking
    // we just close the socket and, therefore, terminate the keepAlive
    // NOTE: Don't close super class
    try {
        if (expected > count) {
        long nskip = (long) (expected - count);
        if (nskip <= available()) {
            long n = 0;
            while (n < nskip) {
            nskip = nskip - n;
            n = skip(nskip);} ...

越来越多的在我看来,有在JDK本身就是一个错误。 不幸的是,这是非常难以重现此...

Answer 1:

实施KeepAliveStream您已链接,违反了合同,据此, available()skip()保证是非阻塞的,因此可能确实块。

的可用的()中的合同保证单个非阻塞skip()

返回可以从该输入流不被此输入流的方法的下一呼叫者无阻塞读取(或跳过)的字节数的估计值。 接下来的调用者可能是同一个线程,另一个线程。 单个读或跳过此许多字节不会堵塞,但是可以读或跳过更少的字节。

哪来的实现来电skip()每一次调用多次以available()

    if (nskip <= available()) {
        long n = 0;
        // The loop below can iterate several times,
        // only the first call is guaranteed to be non-blocking. 
        while (n < nskip) { 
        nskip = nskip - n;
        n = skip(nskip);
        }

这并不能证明你的应用程序块,因为KeepAliveStream错误地使用InputStream 。 的某些实现InputStream可能提供更有力的无阻塞的保证,但我认为这是一个非常可能的犯罪嫌疑人。

编辑:更多的研究,这是一个JDK最近修正了: https://bugs.openjdk.java.net/browse/JDK-8004863?page=com.atlassian.jira.plugin.system.issuetabpanels:全个tabpanel 。 错误报告说,大约一个无限循环,但阻塞skip()也可能是一个结果。 此修复程序似乎为解决这两个问题(只有一个单skip()available()



Answer 2:

我想这skip()上的close()的目的是保持有效的支持。

见http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html 。

在此之前的Java SE 6,如果应用程序当多于小数据量保持待读取关闭一个HTTP的InputStream,则连接不得不关闭,而不是被缓存。 现在,Java SE 6中,该行为是读取高达512 KB的关闭在后台线程的连接,从而允许重复使用的连接。 可读取的数据的确切量是通过所述可配置http.KeepAlive.remainingData系统属性。

因此,保持活着可以被有效禁止http.KeepAlive.remainingData=0http.keepAlive=false 。 但是,这可能对性能造成负面影响,如果你总是解决到同一http://t.co主机。

作为@artbristol建议,使用HEAD代替GET似乎是这里最好的解决办法。



Answer 3:

我面临当我试图做一个“HEAD”请求类似的问题。 为了解决这个问题,我删除了“HEAD”方法,因为我只是想ping通网址



文章来源: Java HttpURLConnection InputStream.close() hangs (or works too long?)