Перейти к основному содержимому

Тайм-аут в Apache HttpClient

· 5 мин. чтения

1. Обзор

В этом руководстве показано, как настроить тайм-аут с помощью Apache HttpClient 4 .

Если вы хотите копнуть глубже и узнать о других интересных вещах, которые можно делать с помощью HttpClient, — перейдите к основному руководству по HttpClient .

2. Настройка таймаутов до HttpClient 4.3

2.1. Необработанные строковые параметры

До выхода версии 4.3 HttpClient поставлялся с большим количеством параметров конфигурации, и все они могли быть установлены общим способом, подобным карте.

Нужно было настроить 3 параметра тайм-аута :

DefaultHttpClient httpClient = new DefaultHttpClient();

int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
httpParams.setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000);
httpParams.setParameter(
CoreConnectionPNames.SO_TIMEOUT, timeout * 1000);
httpParams.setParameter(
ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000));

2.2. API

Более важные из этих параметров, а именно первые два, также могут быть установлены через более типобезопасный API:

DefaultHttpClient httpClient = new DefaultHttpClient();

int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(
httpParams, timeout * 1000); // http.connection.timeout
HttpConnectionParams.setSoTimeout(
httpParams, timeout * 1000); // http.socket.timeout

У третьего параметра нет пользовательского установщика в HttpConnectionParams , и его все равно нужно будет установить вручную с помощью метода setParameter .

3. Настройте тайм-ауты с помощью новой версии 4.3. Строитель

Плавный API-интерфейс конструктора, представленный в версии 4.3, обеспечивает правильный способ установки тайм-аутов на высоком уровне :

int timeout = 5;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client =
HttpClientBuilder.create().setDefaultRequestConfig(config).build();

Это рекомендуемый способ настройки всех трех тайм-аутов безопасным для типов и удобочитаемым способом.

4. Объяснение свойств тайм-аута

Теперь давайте объясним, что означают эти различные типы тайм-аутов:

  • Connection Timeout ( http.connection.timeout ) – время установления соединения с удаленным хостом
  • Socket Timeout ( http.socket.timeout ) – время ожидания данных – после установления соединения; максимальное время бездействия между двумя пакетами данных
  • Таймаут диспетчера соединений ( http.connection -manager.timeout ) — время ожидания соединения от менеджера/пула соединений

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

5. Использование HttpClient

После настройки теперь мы можем использовать клиент для выполнения HTTP-запросов:

HttpGet getMethod = new HttpGet("http://host:8080/path");
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
"HTTP Status of response: " + response.getStatusLine().getStatusCode());

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

Обратите внимание, что тайм-аут подключения приведет к возникновению исключения org.apache.http.conn.ConnectTimeoutException , а тайм-аут сокета приведет к возникновению исключения java.net.SocketTimeoutException .

6. Жесткий тайм-аут

Хотя установка тайм-аутов при установлении HTTP-соединения и отсутствии получения данных очень полезна, иногда нам нужно установить жесткий тайм-аут для всего запроса .

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

HttpClient не имеет какой-либо конфигурации, которая позволяет нам установить общий тайм-аут для запроса; однако он обеспечивает функциональность прерывания для запросов , поэтому мы можем использовать этот механизм для реализации простого механизма тайм-аута:

HttpGet getMethod = new HttpGet(
"http://localhost:8080/httpclient-simple/api/bars/1");

int hardTimeout = 5; // seconds
TimerTask task = new TimerTask() {
@Override
public void run() {
if (getMethod != null) {
getMethod.abort();
}
}
};
new Timer(true).schedule(task, hardTimeout * 1000);

HttpResponse response = httpClient.execute(getMethod);
System.out.println(
"HTTP Status of response: " + response.getStatusLine().getStatusCode());

Мы используем java.util.Timer и java.util.TimerTask для настройки простой отложенной задачи, которая прерывает HTTP-запрос GET после 5-секундного жесткого тайм-аута.

7. Тайм-аут и циклический перебор DNS — кое-что, о чем нужно знать

Довольно часто некоторые более крупные домены используют конфигурацию DNS с циклическим перебором, когда один и тот же домен сопоставляется с несколькими IP-адресами . Это вводит новую проблему для тайм-аута для такого домена просто из-за того, как HttpClient попытается подключиться к этому домену, который истекает:

  • HttpClient получает список IP-маршрутов к этому домену .
  • он пробует первый - время ожидания истекло (с настроенными нами тайм-аутами)
  • он пытается второй - это также тайм-аут
  • и так далее …

Итак, как видите , вся операция не истечет по таймауту, как мы ожидаем . Вместо этого - время ожидания истечет, когда все возможные маршруты истекут. Более того — это будет происходить совершенно прозрачно для клиента (если только у вас не настроен лог на уровне DEBUG).

Вот простой пример, который вы можете запустить и воспроизвести эту проблему:

int timeout = 3;
RequestConfig config = RequestConfig.custom().
setConnectTimeout(timeout * 1000).
setConnectionRequestTimeout(timeout * 1000).
setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(config).build();

HttpGet request = new HttpGet("http://www.google.com:81");
response = client.execute(request);

Вы заметите логику повторных попыток с уровнем журнала DEBUG:

DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address

DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address

DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address
//...

8. Заключение

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

Реализацию этих примеров можно найти в проекте GitHub .