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

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

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

所以,我们只是按照重定向。 而已。 我们不会从连接中读取任何数据。 在我们得到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  (a java.io.BufferedInputStream) at sun.net.www.MeteredStream.skip(MeteredStream.java:134) - locked  (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()之前将其清理回连接池(如果设置了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);} ... 

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

您链接的KeepAliveStream的实现违反了保证available()skip()保持非阻塞的合同,因此可能确实阻止。

available()的契约保证了一个非阻塞的skip()

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

每次调用available()实现调用skip()多次:

  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); } 

这并不能certificate您的应用程序阻塞,因为KeepAliveStream错误地使用了InputStreamInputStream一些实现可能提供更强的非阻塞保证,但我认为这是一个非常可能的怀疑。

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

我想close() skip()上的skip()用于Keep-Alive支持。

请参阅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可以有效禁用keep alive。 但是,如果您始终使用同一个http://t.co主机,则会对性能产生负面影响。

正如@artbristol建议的那样,使用HEAD而不是GET似乎是最好的解决方案。

当我试图提出“HEAD”请求时,我遇到了类似的问题。 为了解决这个问题,我删除了“HEAD”方法,因为我只想pingurl