Автор оригинала: Krzysztof Woyke.
1. Обзор
В этом кратком руководстве мы сосредоточимся на различных типах тайм-аутов, которые мы можем установить для клиента OkHttp .
Для получения более общего обзора библиотеки OkHttp ознакомьтесь с нашим вводным руководством по OkHttp .
2. Тайм-аут подключения
Тайм-аут подключения определяет период времени, в течение которого наш клиент должен установить соединение с целевым хостом .
По умолчанию для OkHttpClient этот тайм-аут установлен на 10 секунд .
Однако мы можем легко изменить его значение, используя OkHttpClient.Конструктор#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.Конструктор#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.Конструктор#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#время ожидания вызова метод.
Давайте рассмотрим пример практического использования:
@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#new Builder . При этом создается новый клиент с теми же настройками. Затем мы можем использовать методы конструктора для настройки параметров тайм-аута по мере необходимости.
Давайте теперь посмотрим, как это сделать на практике:
@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); }
Как мы видим, клиент по умолчанию не смог завершить HTTP-вызов из-за превышенного времени ожидания чтения.
Вот почему мы создали клиент extended Timeout, скорректировали значение тайм-аута и успешно выполнили запрос.
7. Резюме
В этой статье мы исследовали различные таймауты, которые мы можем настроить для OkHttpClient .
Мы также кратко описали, когда таймауты подключения, чтения и записи применяются во время HTTP-вызова.
Кроме того, мы показали, как легко изменить определенное значение тайм-аута только для одного запроса .
Как обычно, все примеры кода доступны на GitHub .