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 .