Процесс Java зависает на IOUtils. Подозреваемый тупик

У меня есть процесс Java, который зависает при вызове IOUtils.toString со следующим кодом:

String html = "";
try {
    html = IOUtils.toString(someUrl.openStream(), "utf-8"); // process hangs on this line
} catch (Exception e) {
    return null;
}

Он не может воспроизвести это достоверно. Это часть поискового робота, поэтому он успешно выполняет эту строку тысячи раз, но в конечном итоге приводит к тому, что процесс зависает здесь через несколько дней.

Вывод из jstack:

2013-09-25 09:09:36
Full thread dump OpenJDK 64-Bit Server VM (20.0-b12 mixed mode):

"Attach Listener" daemon prio=10 tid=0x00007f2b1c001000 nid=0x225a waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-0" prio=10 tid=0x00007f2b34122000 nid=0x187b runnable [0x00007f2b30970000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(SocketInputStream.java:146)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:275)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
        - locked <0x00000000e3d2d160> (a java.io.BufferedInputStream)
        at sun.net.www.http.ChunkedInputStream.readAheadBlocking(ChunkedInputStream.java:552)
        at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:609)
        at sun.net.www.http.ChunkedInputStream.read(ChunkedInputStream.java:696)
        - locked <0x00000000e3d30558> (a sun.net.www.http.ChunkedInputStream)
        at java.io.FilterInputStream.read(FilterInputStream.java:133)
        at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:2582)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:282)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:324)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:176)
        - locked <0x00000000e3d317d0> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.Reader.read(Reader.java:140)
        at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1364)
        at org.apache.commons.io.IOUtils.copy(IOUtils.java:1340)
        at org.apache.commons.io.IOUtils.copy(IOUtils.java:1315)
        at org.apache.commons.io.IOUtils.toString(IOUtils.java:525)

Я не вижу способа установить тайм-аут для метода toString. Какие-либо предложения? Это ошибка в Apache Commons? Или, возможно, в моем OpenJDK?


person matt burns    schedule 25.09.2013    source источник
comment
Может быть, 'someUrl' разделен между потоками?   -  person Sergey Morozov    schedule 25.09.2013
comment
ioutils с открытым исходным кодом. Прикрепите отладчик, приостановите виртуальную машину и посмотрите, где она застряла.   -  person Joeri Hendrickx    schedule 25.09.2013


Ответы (4)


Ваш вызов toString() в конечном итоге перенаправляется на copyLarge(). Здесь вы можете видеть, что чтение из потока продолжается до тех пор, пока функция InputStream.read() не обнаружит маркер конца файла (EOF). Согласно этому сообщению читать( ) может считывать 0 байтов, т. е. если URLConnection, из которого вы читаете, не возвращает маркер EOF, метод, вероятно, продолжает считывать 0 байтов навсегда.

Может быть, вы можете отследить, какой URL вызывает проблему?

В любом случае, чтобы реализовать тайм-аут, вы можете начать каждое чтение в отдельном потоке и убить этот поток по истечении определенного времени.

person Sven Amann    schedule 25.09.2013
comment
Ах, спасибо. Меня не волнует, какой URL-адрес вызвал это, но это может помочь мне разработать воспроизводимый тест. Я пробую гуаву в качестве альтернативы, потому что она проста, но если это не сработает, то я думаю, что запуск ее в отдельном потоке может быть моим единственным вариантом. - person matt burns; 25.09.2013

Вместо этого я решил попробовать просто использовать ввод-вывод guava, так как он уже был в моем пути к классам:

String html = "";
try {
    InputSupplier<? extends InputStream> supplier = Resources
            .newInputStreamSupplier(metaUrl);
    html = CharStreams.toString(CharStreams.newReaderSupplier(supplier,
            Charsets.UTF_8));
} catch (Exception e) {
    return null;
}

Обычно для сбоя требуется несколько дней, поэтому, если я не обновлю этот ответ через несколько дней, предположим, что это сработало!

Обновление: 7 дней без зависаний... :)

person matt burns    schedule 25.09.2013
comment
спасибо за публикацию решения! Я получил это в 2017 году, поэтому мне интересно, почему это до сих пор не исправлено. - person tObi; 24.03.2017

У меня такая же проблема. Возможно, это решается с помощью гуавы, но, на мой взгляд, корень проблемы в том, что в сокете не настроен soTimeout.

пытаться

socket.setSoTimeout(10000)

для создания исключения SocketTimeoutException, когда через 10 секунд не будет EOF.

person tObi    schedule 25.03.2017

Собственный метод Java:

InputStream in = новый URL(url).openStream();

Метод гуавы:

Поставщик InputSupplier = Resources.newInputStreamSupplier (новый URL-адрес (url-адрес)); InputStream in = supplier.getInput();

Оба они будут вызывать исключение Connection timed out Exception. Поскольку guave также использует URL.openStream()

Но какой-то сайт настолько медленный, что я каждый раз могу прочитать с него немного данных, и так много раз все еще не доходит до конца. И я также вижу, что Jstack висит там.

Вот так (может быть, только медленно на моем хосте): адрес текстового файла

person Jun.wan    schedule 24.08.2015