Мы уже затронули важность тайм-аутов и описали самые важные связанные ручки JDBC. Следующий аспект тайм-аутов, на котором я хотел бы остановиться, — это использование клиентов API. В частности, HTTP-клиенты, которые на сегодняшний день являются наиболее популярными. Мы рассмотрим несколько популярных библиотек HTTP-клиентов и их конфигурацию в отношении тайм-аутов.

Время ожидания HttpURLConnection

HttpURLConnection доступен с тех пор, как JDK 1.1 получил возможность тайм-аута сетевого взаимодействия в версии JDK 5. 2 доступных тайм-аута setConnectionTimeout, setReadTimeout определяют, как долго ждать до установления соединения и как долго ждать данных с сервера соответственно. Значения по умолчанию: бесконечность ‼️.

Время ожидания Apache HttpClient

HttpClient из пакета Apache HttpComponents был стандартным выбором для связи по протоколу http. Это зрелый проект с богатым API, который устраняет многие HttpURLConnection недостатки, например. пул соединений. Многие из API устарели, например. DefaultHttpClient, org.apache.http.params.CoreConnectionPNames поэтому нужно быть осторожным при установке тайм-аутов, которые они возвращают к системным значениям по умолчанию на уровне сокета.

Доступны 3 настройки времени ожидания:

val requestConfig = RequestConfig.custom()
    // Determines the timeout in milliseconds until a connection is established.
    .setConnectTimeout(5_000) 
    // Defines the socket timeout in milliseconds,
    // which is the timeout for waiting for data or, put differently,
    // a maximum period inactivity between two consecutive data packets).
    .setSocketTimeout(5_000)
    // Returns the timeout in milliseconds used when requesting a connection
    // from the connection manager.
    .setConnectionRequestTimeout(2_000)
    .build()

requestConfig можно использовать по умолчанию для экземпляра HttpClient:

val httpClient = HttpClients.custom()
    .setDefaultRequestConfig(requestConfig)
    .build()

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

val get = HttpGet("http://httpbin.org/get").apply { 
    config = requestConfig
}
httpClient.execute(get)

OkHttp

OkHttp — мой любимый клиент HTTP и HTTP/2 для приложений Android и Java. Он эффективен и имеет хорошие настройки по умолчанию. Доступны 3 настройки времени ожидания:

val client = OkHttpClient.Builder()
    // Sets the default connect timeout for new connections.
    .connectTimeout(5, TimeUnit.SECONDS)
    // Sets the default read timeout for new connections.
    .readTimeout(10, TimeUnit.SECONDS)
    // Sets the default write timeout for new connections.
    .writeTimeout(20, TimeUnit.SECONDS)
    .build()

Все connectTimeout, readTimeout и writeTimeout по умолчанию равны 10 секундам 👍.

Тайм-ауты XMLHttpRequest и Fetch API

XMLHttpRequest является стандартной основой сетевого взаимодействия веб-приложений уже более 10 лет. В настоящее время он заменяется Fetch API, но он по-прежнему остается и будет оставаться самым популярным в течение нескольких лет. В XMLHttpRequest доступна только одна конфигурация timeout:

Свойство XMLHttpRequest.timeout представляет собой тип long без знака, представляющий количество миллисекунд, которое может занять запрос, прежде чем он будет автоматически завершен. Значение по умолчанию – 0, что означает отсутствие тайм-аута.

По умолчанию бесконечен ‼️

Поскольку значение по умолчанию не настроено, мы должны тщательно установить тайм-аут в нашем коде! Может возникнуть соблазн подумать, что тайм-аут на стороне клиента не так важен по сравнению с тайм-аутом на сервере. Мягко говоря, это сомнительное отношение. Мы должны иметь в виду, что существует жесткое ограничение на количество подключений браузера к одному домену, что очень важно, если мы используем HTTP 1.*. Когда мы достигнем максимального количества одновременно открытых подключений, любое новое XMLHttpRequest будет стоять в очереди на неопределенный срок. Значение ограничения варьируется в браузерах, и недавний RCF ослабляет его. HTTP/2 решает проблему с мультиплексированием соединений, тем не менее его распространение все еще невелико. По данным w3techs это около 20% на сегодняшний день. Значение тайм-аута, используемое в XMLHttpRequest, еще более важно в одностраничных приложениях. В SPA XMLHttpRequest без тайм-аута может существовать до тех пор, пока сервер и промежуточные сетевые стороны позволяют эффективно блокировать все последующие сетевые вызовы.

Fetch API призван заменить XMLHttpRequest. Таким образом, печально, что возможность тайм-аута запроса еще не стала стандартом. В настоящее время не существует стандартного способа установить тайм-аут. Есть несколько активных проблем GitHub: Добавить параметр тайм-аута, Добавить параметр для автоматического отклонения обещания выборки по истечении определенного времени, которые охватывают возможные решения. Было предложение по отменяемым обещаниям, которое было отозвано после много обсуждений и отсутствия консенсуса. Совершенно новый способ, недавно реализованный Edge и Firefox, позволяет прерывать вызов Fetch API 🎉 через стандартизированную DOM AbortController. Надеюсь, он скоро войдет в стандарт Fetch API.

const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => controller.abort(), 5000);
fetch(url, { signal }).then(response => {
  return response.text();
}).then(text => {
  console.log(text);
});

Время ожидания сеанса URL

URLSession является преемником NSURLConnection, который лежит в основе большинства, если не всех http-клиентов iOS, например. Аламофайр. Существует 2 основных значения времени ожидания для настройки, оба из которых имеют значения по умолчанию, доступные через URLSessionConfiguration.default:

let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 20.0
sessionConfig.timeoutIntervalForResource = 40.0
let session = URLSession(configuration: sessionConfig)

К счастью, настроены значения по умолчанию:

  • timeoutIntervalForRequest:

Это свойство определяет интервал времени ожидания запроса для всех задач в сеансах на основе этой конфигурации. Интервал времени ожидания запроса определяет, как долго (в секундах) задача должна ждать получения дополнительных данных, прежде чем сдаться. Таймер, связанный с этим значением, сбрасывается всякий раз, когда поступают новые данные. Значение по умолчанию — 60.

  • timeoutIntervalForResource:

Это свойство определяет интервал времени ожидания ресурса для всех задач в сеансах на основе этой конфигурации. Интервал времени ожидания ресурса определяет, как долго (в секундах) ждать передачи всего ресурса, прежде чем отказаться от него. Значение по умолчанию — 7 дней.

Обратите внимание, что timeoutIntervalForResource — это тайм-аут более высокого уровня, чем то, что мы рассматривали в других HTTP-клиентах. Он включает в себя повторные попытки и / или тайм-ауты запросов, поэтому имеет большое значение по умолчанию.

Резюме

Многие HTTP-клиенты не имеют хорошей конфигурации тайм-аута по умолчанию. Следовательно, если вы заботитесь об использовании ресурсов вашего приложения и стабильности системы, вы должны тщательно просмотреть и настроить тайм-ауты, где это применимо. Отрадно видеть, что современные HTTP-клиенты, например. OkHttp и URLSession имеют короткий, но разумный вариант по умолчанию.

Первоначально опубликовано на brightinventions.pl

Пётр Мионсковски,поклонник TDD, стремящийся узнать что-то новое

Личный блог Электронная почта Twitter Github Stackoverflow

Эта статья кросспостирована с личным блогом автора.