Java HttpURLConnection InputStream.close()挂起(或工作时间过长?)
首先,一些背景。有一个工作程序可以扩展/解析一堆短URL:Java HttpURLConnection InputStream.close()挂起(或工作时间过长?),java,http,tcp,httpurlconnection,Java,Http,Tcp,Httpurlconnection,首先,一些背景。有一个工作程序可以扩展/解析一堆短URL: http://t.co/example -> http://example.com 所以,我们只需遵循重定向。就这样。我们不从连接中读取任何数据。在我们得到200之后,我们返回最终的URL并关闭InputStream 现在是问题本身。在生产服务器上,InputStream.close()调用中挂起了一个解析器线程。调用: "ProcessShortUrlTask" prio=10 tid=0x00007f8810119000 n
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 <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 ...
在我看来,JDK本身似乎有一个bug。不幸的是,很难重现这个…我想close()
上的skip()
是为了保持活动支持
看
在JavaSE6之前,如果应用程序在
还有一小部分数据需要读取,然后
连接必须关闭,而不是缓存。现在在JavaSE中
6,其行为是在一个内存中读取最多512 KB的连接
后台线程,从而允许重新使用连接。这个
可读取的确切数据量可通过
http.KeepAlive.remainingData
system属性
因此,可以使用http.KeepAlive.remainingData=0
或http.KeepAlive=false
有效禁用keep alive。
但是,如果总是寻址到同一个主机,这可能会对性能产生负面影响
正如@artbristol所建议的,在这里使用HEAD而不是GET似乎是更好的解决方案。您链接的
KeepAliveStream
的实现违反了合同,根据该合同,available()
和skip()
保证是非阻塞的,因此可能确实是阻塞的
保证单个无阻塞skip():
返回可读取字节数的估计值(或
跳过)而不会被下一个
此输入流的方法的调用方。下一个来电者可能是
同一条线或另一条线。一次读取或跳过此文件
许多字节不会阻塞,但可以读取或跳过更少的字节
其中,实现每次调用available()
多次调用skip()
:
if(nskip我在尝试发出“HEAD”请求时遇到了类似的问题。为了解决这个问题,我删除了“HEAD”方法,因为我只想ping url您是否尝试过直接使用InputStream而不是BufferedInputStream?流是从HttpURLConnection返回的。getInputStream()呼叫。我无法控制它。我明白了。好吧,这只是一个意外。你能用HEAD
而不是GET
?是的,在某些情况下,我们可以使用HEAD作为非常好的优化。不,我们不能完全摆脱GET。原因有两个。首先,不是每个网站都支持HEAD。其次,我们需要尽可能多地模拟浏览器行为。Even如果某个站点支持HEAD,它可能返回与GET不同的内容。感谢您指向remainingData
选项。很高兴知道。虽然问题没有解决。而且,我完全不明白为什么close()
挂起。文档说明:在后台线程中读取高达512 KB的数据。即使站点速度太慢,无法在两天内为我们提供512 KB的数据,“后台”线程如何挂起应用程序?是的,我们不想完全禁用连接缓存(我们知道keepAlive=false
和其他一些允许关闭它的缓存设置)。另请参阅我对@artbristol关于HEAD的评论。谢谢你,Jan。事实上,这是一个非常合理的解释。你知道JDK开发社区中是否有反向移植的做法吗?有没有可能在JDK6/7中修复此问题?@Shcheklein根据本网站的信息,Java SE版本会更新错误修复、安全修复和针对或者至少3年的期限…’。对于Java 6,这一期限将于2013年2月结束,但Java 7应在2014年7月之前接受错误修复。我不清楚这一特定修复是否为市场v8,也许它被认为优先级太低(P4)。如果您对该错误发表评论并解释它是在实际应用程序中遇到的,以及他们是否计划修复v7,可能会有所帮助,因为根据他们应该执行的策略。您显示的代码在close()中,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);} ...
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);
}