首先,一些背景知识。有一个工作人员可以扩展/解决一堆短网址:
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 ...
经过简短的研究,我了解到skip()
被调用以在将流发送回连接池之前清理流(如果设置了keep-alive?)。我仍然不明白如何避免这种情况。此外,我怀疑我们的代码中是否存在一些不良设计或JDK中存在问题。
所以,问题是:
close()
上?保证一些合理的
例如,超时。 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本身存在一个错误。不幸的是,重现这个很难......
答案 0 :(得分:5)
您已关联的KeepAliveStream
的实施违反了available()
和skip()
保证不会阻止的合同,因此可能会阻止。
The contract of 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);
}
这并不能证明您的应用会阻止,因为KeepAliveStream
错误地使用了InputStream
。 InputStream
的某些实现可能会提供更强大的非阻塞保证,但我认为这很可能是怀疑。
编辑:经过一番研究,这是JDK中最近修复的错误:https://bugs.openjdk.java.net/browse/JDK-8004863?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel。错误报告说有关无限循环,但阻塞skip()
也可能是结果。该解决方案似乎解决了这两个问题(每skip()
只有一个available()
)
答案 1 :(得分:2)
我想skip()
上close()
的{{1}}用于支持Keep-Alive。
请参阅http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html。
在Java SE 6之前,如果应用程序何时关闭HTTP InputStream 超过少量数据仍然需要读取,然后 必须关闭连接,而不是缓存。现在在Java SE中 6,行为是在a中读取最多512 KB的连接 后台线程,从而允许重用连接。该 可读取的确切数据量可通过以下方式配置
http.KeepAlive.remainingData
系统属性。
因此可以使用http.KeepAlive.remainingData=0
或http.keepAlive=false
有效禁用保持活动状态。
但是,如果您始终寻址到同一个http://t.co主机,则会对性能产生负面影响。
正如@artbristol建议的那样,使用HEAD而不是GET似乎是最好的解决方案。
答案 2 :(得分:0)
当我试图提出“HEAD”请求时,我遇到了类似的问题。为了解决这个问题,我删除了“HEAD”方法,因为我只想ping网址