Раздел 11 · Network

HTTP/2, HTTP/3 (QUIC) и сетевая инфраструктура

senior~35 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

HTTP/2, HTTP/3 (QUIC) и сетевая инфраструктура

HTTP/1.1 прожил больше двадцати лет, и всё это время у него была одна и та же болезнь: по одному TCP-соединению нельзя честно гнать несколько запросов параллельно. Браузеры лечили это костылями (несколько соединений к домену, склейка ассетов), но болезнь оставалась. HTTP/2 и HTTP/3 это две попытки вылечить её по-настоящему, и вторая дошла до того, что выкинула TCP.

Это финальный урок раздела. Сначала разберём, как менялся сам протокол, потом поднимемся на уровень выше кода, к маршрутизации, CDN и VPN, которые переносят все эти пакеты между континентами.

Болезнь HTTP/1.1: head-of-line blocking

У HTTP/1.1 четыре врождённые болячки, и все растут из одного корня: по одному TCP-соединению запросы идут строго по очереди.

  1. Head-of-line blocking. Конвейер (pipelining) теоретически разрешал слать несколько запросов, не дожидаясь ответов, но ответы обязаны вернуться в том же порядке. Застрял первый, ждут все за ним. Это head-of-line blocking. Из-за него pipelining на практике почти никто не включал, он по умолчанию выключен в браузерах.
  2. Дорогие соединения. Каждое новое соединение это рукопожатия TCP и TLS, несколько round-trip до первого байта. Чтобы грузить страницу параллельно, браузер открывал по 6 соединений на домен, и каждое платило за старт.
  3. Заголовки без сжатия. Cookie, User-Agent, Accept шлются целиком в каждом запросе. На странице с сотней ассетов это килобайты повторов на каждый запрос.
  4. Нет приоритетов. Сервер не знает, что CSS важнее аналитики, и отдаёт в порядке поступления.

Лечили это костылями на стороне фронтенда: открывали несколько соединений к домену, размазывали ассеты по поддоменам (domain sharding), склеивали скрипты в бандлы, картинки в CSS-спрайты, мелкие ресурсы инлайнили в HTML. Всё это лечение симптомов, а не причины, и в эпоху HTTP/2 такие приёмы из полезных стали вредными.

HTTP/2: мультиплексирование поверх одного TCP

HTTP/2 переписал транспорт под капотом, оставив семантику (методы, статусы, заголовки) прежней. Ключевые идеи:

  • Бинарные фреймы вместо текста.
  • Мультиплексирование: десятки запросов по одному соединению одновременно, ответы приходят как готовы, без очереди.
  • HPACK сжатие заголовков: повторяющиеся Cookie и User-Agent не гоняются целиком.
  • Server push (сервер сам шлёт ресурсы наперёд) появился, не прижился и фактически удалён, на практике его не используют.

Версию протокола на TLS-рукопожатии выбирает ALPN.

Как устроен мультиплексинг изнутри

Магия мультиплексирования это всего лишь нарезка на фреймы. Каждое сообщение HTTP/2 разбивается на кадры фиксированного формата: 9 байт заголовка (длина, тип, флаги, stream id) плюс полезная нагрузка. Типы кадров, которые стоит знать:

  • HEADERS заголовки запроса или ответа.
  • DATA кусок тела.
  • stream это логический поток: HEADERS открывает его, DATA-кадры досылают тело, и у всех кадров одного потока общий нечётный stream id.
  • SETTINGS, WINDOW_UPDATE (управление потоком, flow control на уровне stream), RST_STREAM (отмена одного потока без разрыва соединения), PRIORITY (подсказка приоритета).

Ключевой трюк: кадры разных stream свободно чередуются на одном проводе. Приёмник смотрит на stream id в заголовке кадра и собирает перемешанный поток обратно в нужные сообщения. Поэтому медленный крупный ответ больше не держит очередь: его DATA-кадры просто едут вперемешку с кадрами быстрого ответа.

Покрути переключатель и пошагай: тот же набор кадров, но в HTTP/1.1 они уходят строго по очереди, а в HTTP/2 чередуются.

Поэтому старые приёмы оптимизации в HTTP/2 стали вредными: domain sharding плодит лишние соединения и мешает мультиплексированию, а большие бандлы рушат кеширование. С HTTP/2 правильнее много мелких ресурсов по одному соединению.

HOL вернулся, но ниже

Но осталась тонкость: мультиплексирование живёт поверх TCP, а TCP это единый упорядоченный поток. Потеряли один пакет, и TCP тормозит доставку всем логическим потокам сразу, пока не переотправит. Head-of-line blocking никуда не делся, он просто спустился с прикладного уровня на транспортный. Виджет выше показывает идеальный случай без потерь, но стоит одному TCP-сегменту потеряться, и все stream замирают вместе.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.