9

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

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()在将流发送回连接池之前调用它来清理流(如果设置了保持活动状态?)。我仍然不明白如何避免这种情况。此外,我怀疑我们的代码中是否存在一些糟糕的设计,或者 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 本身存在越来越多的错误。不幸的是,很难重现这个......

4

3 回答 3

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

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

编辑:经过更多研究,这是 JDK 中最近修复的错误:https://bugs.openjdk.java.net/browse/JDK-8004863?page=com.atlassian.jira.plugin.system.issuetabpanels:所有标签面板。错误报告中提到了无限循环,但skip()也可能导致阻塞。该修复似乎解决了这两个问题(只有一个skip()per available()

于 2013-01-30T16:02:32.793 回答
2

我猜这skip()close()为了支持 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=0or有效地禁用保持活动状态http.keepAlive=false。但是,如果您总是寻址到同一个http://t.co主机,这可能会对性能产生负面影响。

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

于 2013-01-17T14:28:50.743 回答
0

当我尝试发出“HEAD”请求时,我遇到了类似的问题。为了修复它,我删除了“HEAD”方法,因为我只是想 ping url

于 2014-11-02T10:06:46.420 回答