HTTP/3: руйнування основ і чудовий новий світ

Ось уже більше 20 років ми дивимося веб-сторінки за протоколом HTTP. Більшість користувачів взагалі не замислюється над тим, що це таке і як воно працює. Інші знають, що десь під HTTP є TLS, а під ним TCP, під яким IP і таке інше. А треті – єретики – вважають, що TCP – це минуле століття, їм хочеться чогось швидше, надійнішого та захищеного. Але у своїх спробах винайти новий ідеальний протокол вони повернулися до технологій 80-х років і намагаються побудувати на них свій чудовий новий світ.
HTTP/3: руйнування основ і чудовий новий світ

Трохи історії: HTTP/1.1

У 1997 році протокол обміну текстовою інформацією HTTP версії 1.1 знайшов свій RFC. На той час протокол використовувався браузерами вже кілька років, а новий стандарт протримався ще п'ятнадцять. Протокол працював лише за принципом запит-відповідь і призначався, переважно, передачі текстової інформації.

HTTP був спроектований для роботи поверх протоколу TCP, що гарантує доставку пакетів до адресата. Робота TCP заснована на встановленні та підтримці надійного з'єднання між кінцевими точками та розбиття трафіку на сегменти. Сегменти мають свій послідовний номер та контрольну суму. Якщо раптом якийсь із сегментів не прийде або прийде з невірною контрольною сумою, то передача зупиниться, доки не буде відновлено втрачений сегмент.

У HTTP/1.0 TCP-з'єднання закривалося після кожного запиту. Це було дуже марнотратно, т.к. Встановлення TCP-з'єднання (3-Way-Handshake) це нешвидкий процес. HTTP/1.1 представили механізм keep-alive, який дозволяє перевикористовувати одне з'єднання для декількох запитів. Однак, оскільки воно може легко стати пляшковим шийкою, у різних імплементаціях HTTP/1.1 допускається відкриття декількох TCP-з'єднань до одного хоста. Наприклад, у Chrome та в останніх версіях Firefox допускається до шести з'єднань.
HTTP/3: руйнування основ і чудовий новий світ
Шифрування передбачалося також віддати на відкуп іншим протоколам, і цього поверх TCP став використовуватися протокол TLS, який досить надійно захищав дані, але ще більше збільшував час, необхідне встановлення з'єднання. У результаті процес рукостискання став виглядати так:
HTTP/3: руйнування основ і чудовий новий світ
Ілюстрація Cloudflare

Таким чином HTTP/1.1 мав низку проблем:

  • Повільне встановлення з'єднання.
  • Дані передаються в текстовому вигляді, а значить, передача картинок, відео та іншої нетекстової інформації неефективна.
  • Одне TCP-з'єднання використовується одного запиту, отже інші запити мають або знайти собі інше з'єднання, або чекати, поки поточний запит його відпустить.
  • Підтримується лише pull-модель. У стандарті немає нічого про server-push.
  • Заголовки надсилаються текстом.

Якщо server-push так-сяк реалізується за допомогою протоколу WebSocket, то з іншими проблемами потрібно було розбиратися більш радикально.

Небагато сучасності: HTTP/2

У 2012-му році в надрах Google почалася робота над протоколом SPDY (вимовляється «СНІД»). Протокол мав вирішити основні проблеми HTTP/1.1 і при цьому повинен був зберегти зворотну сумісність. У 2015 році робоча група IETF представила специфікацію HTTP/2, що базується на протоколі SPDY. Ось які відмінності були в HTTP/2:

  • Бінарна серіалізація.
  • Мультиплексування декількох запитів HTTP в одне TCP-з'єднання.
  • Server-push із коробки (без WebSocket).

Протокол став великим кроком уперед. Він сильно виграє у першої версії за швидкістю і не вимагає створення декількох TCP-з'єднань: усі запити до одного хоста мультиплексуються до одного. Тобто в одному з'єднанні є кілька так званих стриму, кожен з яких має свій ID. Бонусом іде коробковий server-push.

Однак мультиплексація веде до іншої наріжної проблеми. Уявіть, що ми виконуємо асинхронно 5 запитів до одного сервера. При використанні HTTP/2 всі ці запити будуть виконуватися в рамках одного TCP-з'єднання, а значить, якщо один із сегментів будь-якого запиту загубиться або прийде неправильно, передача всіх запитів і відповідей зупиниться, доки не буде відновлено сегмент, що загубився. Очевидно, що чим гірша якість з'єднання, тим повільніше працює HTTP/2. За оцінкою Денієла Стенберга, в умовах, коли втрачені пакети становлять 2% від усіх, HTTP/1.1 у браузері показує себе краще, ніж HTTP/2 за рахунок того, що відкриває 6 з'єднань, а не одне.

Ця проблема називається «head-of-line blocking» і, на жаль, вирішити її при використанні TCP неможливо.
HTTP/3: руйнування основ і чудовий новий світ
Ілюстрація Daniel Steinberg

Як наслідок, розробники стандарту HTTP/2 проробили велику роботу і зробили практично все, що можна було зробити на прикладному рівні моделі OSI. Настав час спускатися на транспортний рівень та винаходити новий транспортний протокол.

Нам потрібний новий протокол: UDP vs TCP

Досить швидко стало зрозуміло, що впровадити зовсім новий протокол транспортного рівня – завдання в сучасних реаліях не вирішуване. Справа в тому, що про транспортний рівень знають залізниці або middle-boxes (роутери, файрволи, NAT-сервери…), а навчити їх чогось нового вкрай непросте завдання. Крім того, підтримка транспортних протоколів зашита в ядро ​​операційних систем, а ядра теж змінюються не дуже охоче.

І тут можна було б опустити руки і сказати «Ми, звичайно, винайдемо новий HTTP/3 з преферансом та куртизанками, але впроваджуватиметься він буде 10-15 років (приблизно через такий час буде замінено більшість залозок)», але є ще один не самий очевидний варіант: використовувати протокол UDP. Так-так, той самий протокол, яким ми кидалися файликами по локалці наприкінці дев'яностих-початку нульових. Майже всі сьогоднішні залозки вміють з ним працювати.

У чому переваги UDP у порівнянні з TCP? Насамперед у тому, що ми не маємо сесії транспортного рівня, про яку знає залізо. Це дозволяє нам самим визначати сесію на кінцевих точках і там же розрулювати конфлікти, що виникають. Тобто ми не обмежені однією чи декількома сесіями (як у TCP), а можемо настворювати їх стільки, скільки нам потрібно. По-друге, передача даних UDP відбувається швидше, ніж TCP. Таким чином, теоретично, ми можемо пробити сьогоднішню стелю швидкості, досягнуту в HTTP/2.

Однак, UDP не гарантує надійності передачі даних. Фактично ми просто надсилаємо пакети, сподіваючись, що на іншому кінці їх отримають. Не отримали? Ну не пощастило ... Цього було достатньо для передачі відео для дорослих, але для більш серйозних речей потрібна надійність, а значить, доведеться накрутити щось ще поверх UDP.

Як і у випадку з HTTP/2, робота зі створення нового протоколу розпочалася в Google у 2012-му році, тобто приблизно одночасно з початком роботи над SPDY. 2013-го Джим Роскінд представив широкому загалу протокол QUIC (Quick UDP Internet Connections), а вже 2015-го було внесено Internet Draft для стандартизації в IETF. Вже на той момент протокол, розроблений Роскіндом у Google, сильно відрізнявся від винесеного на стандарт, тому гуглівську версію стали називати gQUIC.

Що таке QUIC

По-перше, як уже було сказано, це обгортка над UDP. Поверх UDP піднімається QUIC-connection, у якому за аналогією з HTTP/2 можуть існувати кілька стриму. Ці стрими існують лише на кінцевих точках та обслуговуються незалежно. Якщо втрата пакета сталася в одному стримі, інші це ніяк не торкнеться.
HTTP/3: руйнування основ і чудовий новий світ
Ілюстрація Daniel Steinberg

По-друге, шифрування тепер реалізовано не окремим рівнем, а включено до протоколу. Це дозволяє встановлювати з'єднання та обмінюватися публічними ключами за одне рукостискання, а також дозволяє використовувати хитрий механізм 0-RTT handshake і взагалі уникнути затримок при рукостисканні. Також можна шифрувати окремі пакети даних. Це дозволяє не чекати на завершення прийому даних зі стриму, а розшифровувати отримані пакети незалежно. Такий режим роботи взагалі неможливий в TCP, т.к. TLS і TCP працювали незалежно друг від друга, і TLS було знати, які шматки буде рубати дані TCP. Отже, не міг підготувати свої сегменти так, щоб вони вкладалися в сегменти TCP один до одного і могли бути розшифровані незалежно. Всі ці покращення дозволяють QUIC знизити latency в порівнянні з TCP.
HTTP/3: руйнування основ і чудовий новий світ
По-третє, концепція легких стримувань дозволяє відв'язати з'єднання від IP-адреси клієнта. Це важливо, наприклад, коли клієнт перемикається з однієї Wi-Fi точки доступу до іншої, змінюючи свій IP. У цьому випадку при використанні TCP відбувається тривалий процес, в ході якого існуючі TCP-з'єднання відвалюються по таймауту і створюються нові з'єднання з нового IP. У випадку з QUIC клієнт просто продовжує посилати серверу пакети з нового IP зі старим ID стриму. Т.к. ID стрим тепер унікальний і не перевикористовується, сервер розуміє, що клієнт змінив IP, надсилає втрачені пакети і продовжує комунікацію за новою адресою.

По-четверте, QUIC реалізується лише на рівні докладання, а чи не операційної системи. Це, з одного боку, дозволяє швидше вносити зміни до протоколу, т.к. щоб отримати оновлення досить просто оновити бібліотеку, а не чекати на нову версію ОС. З іншого боку, це веде до збільшення споживання процесора.

Ну і насамкінець, заголовки. Компресія заголовків саме стосується моментів, які відрізняються в QUIC і gQUIC. Не бачу сенсу присвячувати цьому багато часу, скажу лише, що у версії, поданій на стандартизацію, компресію заголовків зробили максимально схожою на компресію заголовків у HTTP/2. Детальніше можна прочитати тут.

Наскільки воно швидше?

Це складне питання. Справа в тому, що поки що у нас немає стандарту, особливо нема чого вимірювати. Мабуть, єдині статистичні дані, які ми маємо – статистика Гугла, який використовує gQUIC з 2013 року та в 2016 прозвітував перед IETF, Що близько 90% трафіку, що йде до їхніх серверів від браузера Chrome, тепер використовує QUIC. У цій же презентації вони повідомляють, що через gQUIC сторінки завантажуються приблизно на 5% швидше, а в потоковому відео на 30% менше підвісу порівняно з TCP.

У 2017 році група дослідників на чолі з Arash Molavi Kakhki опублікувала велику роботу вивчення продуктивності gQUIC в порівнянні з TCP.
Дослідження виявило кілька слабких сторін gQUIC, таких як нестійкість до перемішування мережних пакетів, жадібність (unfairness) до пропускної спроможності каналу та повільніша передача невеликих (до 10 кб) об'єктів. Останнє, втім, вдається компенсувати за допомогою 0-RTT. У решті досліджених випадках gQUIC показав зростання швидкості проти TCP. Про конкретні цифри тут говорити складно. Краще почитати саме дослідження або короткий піст.

Тут потрібно сказати, що це дані саме про gQUIC, і вони неактуальні для стандарту, що розробляється. Що буде для QUIC: поки що таємниця за сімома печатками, але є надія, що слабкі сторони, виявлені у gQUIC, будуть враховані та виправлені.

Небагато майбутнього: а що з HTTP/3?

А ось тут все кристально ясно: API не зміниться. Все залишиться так само, як і було в HTTP/2. Ну а якщо API залишається незмінною, перехід на HTTP/3 повинен буде вирішуватися використанням на бекенді свіжої версії бібліотеки, що підтримує транспорт QUIC. Щоправда, досить довго доведеться тримати фоллбек на старі версії HTTP, т.к. Інтернет зараз не готовий до повного переходу на UDP

Хто вже підтримує

Ось перелік існуючих реалізацій QUIC. Незважаючи на відсутність стандарту, список непоганий.

Жоден браузер зараз не підтримує QUIC у прод-релізі. Нещодавно була інформація, що в Chrome увімкнули підтримку HTTP/3, але поки що тільки в Canary.

З бекендів HTTP/3 підтримує лише Чайниця и Cloudflare, але поки що експериментально. NGINX наприкінці весни 2019-го оголосили, що почали роботу над підтримкою HTTP/3, але поки що не закінчили.

Які є проблеми

Ми з вами живемо в реальному світі, де жодна велика технологія не може пройти в маси, не зустрівши опору і QUIC тут не виняток.

Найважливіше, потрібно якось пояснити браузеру, що "https://" тепер не факт, що веде на 443-й TCP-порт. Там взагалі може бути TCP. Для цього використовується заголовок Alt-Svc. Він дозволяє повідомити браузеру, що цей веб-сайт також доступний на такому протоколі за такою адресою. Теоретично це має працювати як годинник, але на практиці ми натрапимо на те, що UDP може бути, наприклад, заборонений на файрволі, щоб уникнути DDoS атак.

Але навіть якщо UDP не заборонено, клієнт може перебувати за NAT-роутером, який налаштований на утримання TCP-сесії за адресою IP, а т.к. ми використовуємо UDP, в якому немає апаратної сесії, NAT не буде утримувати з'єднання, і QUIC-сесія буде постійно обриватися.

Всі ці проблеми пов'язані з тим, що UDP раніше не використовувався для передачі інтернет-контенту, і виробники залізниць не могли передбачити, що таке станеться. Так само адміни поки не дуже розуміють, як правильно налаштувати свої мережі для роботи QUIC. Ця ситуація потихеньку змінюватиметься, і, у будь-якому разі, подібні зміни займуть менше часу, ніж запровадження нового протоколу транспортного рівня.

Крім того, як було описано, QUIC сильно збільшує використання процесора. Daniel Stenberg оцінив зростання по процесору до трьох разів.

Коли настане HTTP/3

Cтандарт хочуть прийняти до травня 2020 року, але з огляду на те, що на даний момент недоробленими залишаються документи, заплановані на липень 2019, можна сказати, що дату швидше за все відсунуть.

Ну а Google використовує свою реалізацію gQUIC з 2013 року. Якщо подивитися HTTP-запит, який надсилається гугловому пошуковику, можна побачити ось це:
HTTP/3: руйнування основ і чудовий новий світ

Висновки

QUIC зараз виглядає досить сира, але дуже перспективна технологія. Враховуючи, що останні 20 років усі оптимізації протоколів транспортного рівня стосувалися в основному TCP, QUIC, який у більшості випадків виграє за продуктивністю, виглядає вже зараз вкрай непогано.

Однак поки що залишаються невирішені проблеми, з якими доведеться справлятися в найближчі кілька років. Процес може затягнутися через те, що залучено залізо, яке ніхто не любить оновлювати, проте всі проблеми виглядають цілком вирішуваними, і рано чи пізно у всіх нас буде HTTP/3.

Майбутнє не за горами!

Джерело: habr.com

Додати коментар або відгук