Java HttpURLConnection InputStream.close()挂起(或工作时间太长?)

Shc*_*ein 9 java tcp http httpurlconnection

首先,一些背景.有一个工作人员扩展/解决了一堆短URL:

http://t.co/example -> http://example.com
Run Code Online (Sandbox Code Playgroud)

所以,我们只是按照重定向.而已.我们不会从连接中读取任何数据.在我们得到200之后,我们返回最终的URL并关闭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 ...
Run Code Online (Sandbox Code Playgroud)

经过简单的研究,我明白skip()在将数据流发送回连接池之前会调用它来清理流(如果设置了keep-alive?).我仍然不明白如何避免这种情况.此外,我怀疑代码中是否存在一些不良设计或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);} ...
Run Code Online (Sandbox Code Playgroud)

在我看来,JDK本身存在一个错误.不幸的是,重现这个很难......

Jan*_*bel 5

KeepAliveStream您已链接的实施违反了合同,available()并且skip()保证是非阻塞的,因此可能确实会阻止.

available()的契约保证了一个非阻塞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);
        }
Run Code Online (Sandbox Code Playgroud)

这并不能证明您的应用程序因KeepAliveStream错误使用而阻止InputStream.一些实现InputStream可能提供更强的非阻塞保证,但我认为这是一个非常可能的怀疑.

编辑:经过一番研究,这是JDK最近修复的一个错误:https: //bugs.openjdk.java.net/browse/JDK-8004863 page = com.atlassian.jira.plugin.system.issuetabpanels:全tabpanel.错误报告说有关无限循环,但阻塞skip()也可能是结果.修复似乎解决了这两个问题(skip()每个只有一个available())