HttpClient висит на socketRead0 с успешно выполненным методом
В нашем веб-приложении пользователь может отправить URL. Мы получим данные и проанализируем их на стороне сервера. Для каждого запроса мы используем HttpClient со следующими (соответствующими) настройками
connectionManager.getParams().setConnectionTimeout(10000);
connectionManager.getParams().setSoTimeout(10000);
Когда я вызываю HttpMethod.getResponseBody, код состояния уже проверен на приемлемость. На этом этапе поток зависает с этой трассировкой стека:
java.net.SocketInputStream.socketRead0 ( native code )
java.net.SocketInputStream.read ( SocketInputStream.java:150 )
java.net.SocketInputStream.read ( SocketInputStream.java:121 )
java.io.BufferedInputStream.read1 ( BufferedInputStream.java:273 )
java.io.BufferedInputStream.read ( BufferedInputStream.java:334 )
java.io.FilterInputStream.read ( FilterInputStream.java:133 )
org.apache.commons.httpclient.AutoCloseInputStream.read ( AutoCloseInputStream.java:108 )
java.io.FilterInputStream.read ( FilterInputStream.java:107 )
org.apache.commons.httpclient.AutoCloseInputStream.read ( AutoCloseInputStream.java:127 )
org.apache.commons.httpclient.HttpMethodBase.getResponseBody ( HttpMethodBase.java:690 )
Я не могу найти точный URL, для которого это произошло (был инцидент в живой среде), и я не смог его воспроизвести. Я хотел бы думать, что это просто вопрос сервера, к которому мы подключаемся странно, но, возможно, я что-то упустил. В любом случае, есть ли способ для меня, чтобы запретить вызов метода блокировки навсегда? SoTimeout также является тайм-аутом чтения сокета? Я пропускаю еще одну настройку?
5 ответов
У меня все настройки тайм-аутов просто отлично, но я обнаружил, что у нас есть URL, который выполняет http-чанкинг, но не дает результатов (отлично работает в chrome, но в http-клиенте он зависает всегда, даже с установленным тайм-аутом). К счастью, я владею сервером и просто возвращаю мусор, и он больше не зависает. Это кажется уникальной ошибкой в том, что http-клиент плохо обрабатывает какой-то пустой случай фрагментирования (хотя я мог бы быть в стороне).... Я просто знаю, что он висит каждый раз на том же URL с пустыми данными и этим URL это http chunking csv загрузить обратно в наш http-клиент.
HttpClient различает соединение и запрос. setSoTimeout
настроит время ожидания сокета setConnectionTimeout
настроит как тайм-аут для диспетчера соединений (как долго ждать соединения), так и для установления самого соединения. В предоставленном вами коде вы не устанавливаете тайм-аут для сокета, используемого для самого запроса, и, к сожалению, HttpClient не имеет тайм-аута по умолчанию для этого.
Вот как я делаю это в v4.4.1:
// Configure the socket timeout for the connection, incl. ssl tunneling
connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(200);
connManager.setDefaultMaxPerRoute(100);
SocketConfig sc = SocketConfig.custom()
.setSoTimeout(soTimeoutMs)
.build();
connManager.setDefaultSocketConfig(sc);
HttpClient client = HttpClients.custom()
.setConnectionManager(connManager)
.setConnectionManagerShared(true)
.build();
// configure the timeouts (socket and connection) for the request
RequestConfig.Builder config = = RequestConfig.copy(RequestConfig.DEFAULT);
config.setConnectionRequestTimeout(connectionTimeoutMs);
config.setSocketTimeout(socketTimeoutMs);
HttpRequestBase req = new HttpGet(uri);
req.setConfig(config.build());
client.execute(req);
Мы последовательно видим это в нашей реализации, и похоже, что http-клиент неправильно обрабатывает плохие серверы или что-то в этом роде, и время не истекло..... Я могу воспроизвести в наших настройках этот проект с открытым исходным кодом шины данных, и трассировка стека является немного отличается...
SocketInputStream.socketRead0 (FileDescriptor, byte [], int, int, int) строка: недоступно [собственный метод]
SocketInputStream.read (byte [], int, int) строка: 129
SocketInputBuffer (AbstractSessionInputBuffer).fillBuffer () строка: 166
SocketInputBuffer.fillBuffer () строка: 90
SocketInputBuffer(AbstractSessionInputBuffer).readLine(CharArrayBuffer) строка: 281
Строка DefaultHttpResponseParser.parseHead(SessionInputBuffer): 92
Строка DefaultHttpResponseParser.parseHead(SessionInputBuffer): 62
DefaultHttpResponseParser (AbstractMessageParser).parse () строка: 254
DefaultClientConnection (AbstractHttpClientConnection).receiveResponseHeader () строка: 289
DefaultClientConnection.receiveResponseHeader() строка: 252
BasicPooledConnAdapter (AbstractClientConnAdapter).receiveResponseHeader () строка: 219
HttpRequestExecutor.doReceiveResponse(HttpRequest, HttpClientConnection, HttpContext) строка: 300
HttpRequestExecutor.exxtteContext Http://www.http: HttpRequestExecutortextConnext Http: HttpRequestExecutortextContext:
Строка DefaultRequestDirector.tryExecute(RoutedRequest, HttpContext): 712 Строка DefaultRequestDirector.execute(HttpHost, HttpRequest, HttpContext): 517
Строка DefaultHttpClient(AbstractHttpClient).execute(HttpHost, HttpRequest, HttpContext): 906 Строка DefaultHttpClient(AbstractHttpClient).execute(HttpUriRequest, HttpContext): 805
Строка ReadAggregations.processAggregation(строка, счетчик, счетчик, счетчик, счетчик): 153
Строка ReadAggregations.start(): 96
Строка ReadAggregations.main(String[]): 70
Когда я вызываю HttpMethod.getResponseBody, код состояния уже проверен на приемлемость. В этот момент поток висит
Похоже, у вас есть проблемы с синхронизацией вызовов... вы должны убедиться, что метод
HttpMethod.getResponseBody
вызывается последовательно или должен использовать мьютекс (семафор) для части, которая изменяет код состояния
Также вы должны уменьшить лимит времени ожидания, чтобы предотвратить зависания.
Вы можете попытаться прервать запрос с помощью HttpUriRequest#abort(), см. https://hc.apache.org/httpcomponents-client-4.3.x/httpclient/apidocs/org/apache/http/client/methods/HttpUriRequest.html. Однако установка тайм-аута, который не требует перехвата, была бы лучше. Вот связанный вопрос: Установка таймаута в HTTP-клиенте Apache