Вече засегнахме „важността на изчакванията“ и описахме най-важните „свързани 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, но все още е и ще продължи да бъде най-популярният за няколко години. Има само една налична конфигурация timeout в XMLHttpRequest:

Свойството XMLHttpRequest.timeout е unsigned 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);
});

Времето за изчакване на URLSession

URLSession е наследникът на NSURLConnection, който лежи в основата на повечето, ако не и на всички http клиенти на iOS, напр. Alamofire. Има 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

Тази статия е кръстосана с „личен блог на автора“.