1. Обзор
В этом кратком руководстве мы сосредоточимся на различных типах тайм-аутов, которые мы можем установить для клиента OkHttp .
Более общий обзор библиотеки OkHttp можно найти в нашем вводном руководстве по OkHttp .
2. Время ожидания подключения
Время ожидания соединения определяет период времени, в течение которого наш клиент должен установить соединение с целевым хостом .
По умолчанию для OkHttpClient
этот тайм-аут установлен на 10 секунд .
Однако мы можем легко изменить его значение с помощью метода OkHttpClient.Builder#connectTimeout
. Нулевое значение означает полное отсутствие тайм-аута.
Давайте теперь посмотрим, как создать и использовать OkHttpClient
с настраиваемым временем ожидания соединения:
@Test
public void whenConnectTimeoutExceeded_thenSocketTimeoutException() {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("http://203.0.113.1") // non routable address
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}
В приведенном выше примере показано, что клиент создает исключение SocketTimeoutException
, когда попытка подключения превышает настроенное время ожидания.
3. Тайм-аут чтения
Тайм-аут чтения применяется с момента успешного установления соединения между клиентом и целевым хостом.
Он определяет максимальное время бездействия между двумя пакетами данных при ожидании ответа сервера .
Время ожидания по умолчанию, равное 10 секундам , можно изменить с помощью OkHttpClient.Builder#readTimeout
. Аналогично тайм-ауту соединения, нулевое значение указывает на отсутствие тайм-аута.
Давайте теперь посмотрим, как на практике настроить пользовательское время ожидания чтения:
@Test
public void whenReadTimeoutExceeded_thenSocketTimeoutException() {
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(10, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2") // 2-second response time
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}
Как мы видим, сервер не возвращает ответ в течение заданного тайм-аута в 500 мс. В результате OkHttpClient
создает исключение SocketTimeoutException.
4. Время ожидания записи
Тайм-аут записи определяет максимальное время бездействия между двумя пакетами данных при отправке запроса на сервер.
Аналогично, что касается времени ожидания подключения и чтения, мы можем переопределить значение по умолчанию, равное 10 секундам, с помощью OkHttpClient.Builder#writeTimeout
. По соглашению нулевое значение означает полное отсутствие тайм-аута.
В следующем примере мы устанавливаем очень короткое время ожидания записи 10 мс и отправляем на сервер содержимое размером 1 МБ:
@Test
public void whenWriteTimeoutExceeded_thenSocketTimeoutException() {
OkHttpClient client = new OkHttpClient.Builder()
.writeTimeout(10, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2")
.post(RequestBody.create(MediaType.parse("text/plain"), create1MBString()))
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}
Как мы видим, из-за большой полезной нагрузки наш клиент не может отправить тело запроса на сервер в течение заданного тайм-аута. Следовательно, OkHttpClient
выдает SocketTimeoutException
.
5. Время ожидания вызова
Тайм-аут вызова немного отличается от тайм-аутов подключения, чтения и записи, которые мы уже обсуждали.
Он определяет ограничение по времени для полного HTTP-вызова . Это включает в себя разрешение DNS, подключение, запись тела запроса, обработку сервера, а также чтение тела ответа.
В отличие от других тайм-аутов, его значение по умолчанию равно нулю, что подразумевает отсутствие тайм-аута . Но, конечно, мы можем настроить пользовательское значение, используя метод OkHttpClient.Builder#callTimeout
.
Давайте посмотрим на практический пример использования:
@Test
public void whenCallTimeoutExceeded_thenInterruptedIOException() {
OkHttpClient client = new OkHttpClient.Builder()
.callTimeout(1, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2")
.build();
Throwable thrown = catchThrowable(() -> client.newCall(request).execute());
assertThat(thrown).isInstanceOf(InterruptedIOException.class);
}
Как мы видим, время ожидания вызова превышено, и OkHttpClient
выдает InterruptedIOException.
6. Тайм-аут для каждого запроса
Рекомендуется создать один экземпляр OkHttpClient
и повторно использовать его для всех HTTP-вызовов в нашем приложении.
Однако иногда мы знаем, что определенный запрос занимает больше времени, чем все остальные. В этой ситуации нам нужно продлить данный тайм-аут только для этого конкретного вызова .
В таких случаях мы можем использовать метод OkHttpClient#newBuilder
. При этом создается новый клиент с теми же настройками. Затем мы можем использовать методы построителя для настройки параметров тайм-аута по мере необходимости.
Давайте теперь посмотрим, как это сделать на практике:
@Test
public void whenPerRequestTimeoutExtended_thenResponseSuccess() throws IOException {
OkHttpClient defaultClient = new OkHttpClient.Builder()
.readTimeout(1, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/delay/2")
.build();
Throwable thrown = catchThrowable(() -> defaultClient.newCall(request).execute());
assertThat(thrown).isInstanceOf(InterruptedIOException.class);
OkHttpClient extendedTimeoutClient = defaultClient.newBuilder()
.readTimeout(5, TimeUnit.SECONDS)
.build();
Response response = extendedTimeoutClient.newCall(request).execute();
assertThat(response.code()).isEqualTo(200);
}
Как мы видим, defaultClient
не смог завершить вызов HTTP из-за превышения времени ожидания чтения.
Вот почему мы создали extendedTimeoutClient,
скорректировали значение времени ожидания и успешно выполнили запрос.
7. Резюме
В этой статье мы рассмотрели различные тайм-ауты, которые мы можем настроить для OkHttpClient
.
Мы также кратко описали, когда во время HTTP-вызова применяются тайм-ауты подключения, чтения и записи.
Кроме того, мы показали, как легко изменить определенное значение тайм-аута только для одного запроса .
Как обычно, все примеры кода доступны на GitHub .