Транспорт бэкенда
HTTP/2 и HTTP/3: мультиплексирование и QUIC
2012 год. SPDY-эксперимент Google показывает: 40% латентности веб-страниц - это не сервер и не сеть, это сам протокол. HTTP/1.1 работает как касса с одной полосой при очереди из 80 ресурсов. Следующие 10 лет ушли на то, чтобы превратить этот диагноз в два новых стандарта.
- **gRPC в Kubernetes**: все watch-стримы (`kubectl get pods -w`) и bidirectional RPC работают через HTTP/2 streams - мультиплексирование без открытия отдельных TCP-соединений
- **YouTube на мобильном**: connection migration QUIC позволяет не прерывать воспроизведение при переключении с WiFi на LTE - соединение переживает смену IP
- **Cloudflare CDN**: 30% трафика Cloudflare идёт через HTTP/3, экономия 12-37% на latency в зависимости от качества сети
- **Envoy/Nginx upstream**: HTTP/2 connection pooling к upstream-сервисам - один persistent TCP-коннект вместо flood of short-lived connections
HTTP/2: мультиплексирование и потоки
2012 год. Команда Google публикует результаты эксперимента со SPDY. Вывод ошеломляет: 40% латентности обычной веб-страницы - не медленный сервер, не длинная сеть. Это сам протокол. HTTP/1.1 дожидается ответа на каждый запрос перед следующим - касса с одной полосой при очереди из 80 ресурсов. SPDY становится основой HTTP/2, принятого в 2015.
HTTP/1.1 страдает **head-of-line blocking** на уровне протокола: один TCP-коннект = одна активная пара запрос/ответ. Браузер обходит это через 6-8 параллельных TCP-соединений на домен - дорогое решение с множеством handshake'ов. Domain sharding (cdn1.example.com, cdn2.example.com) - костыль, который HTTP/2 сделал антипаттерном.
HTTP/2 меняет модель: одно TCP-соединение несёт **множество параллельных потоков (streams)**. Каждый поток - независимая двунаправленная последовательность фреймов с уникальным stream ID. Запрос к `/api/users` (stream 1) и `/api/posts` (stream 3) летят одновременно - без блокировки друг друга.
Единица обмена в HTTP/2 - **фрейм (frame)**, бинарный, а не текстовый. Типы: `DATA` (тело), `HEADERS` (заголовки), `PRIORITY`, `RST_STREAM` (отмена), `SETTINGS`, `PING`, `GOAWAY`, `WINDOW_UPDATE`. Каждый фрейм содержит длину, тип, флаги и 31-битный stream ID.
**Stream приоритизация** позволяет сигнализировать серверу: CSS и JS критичны для рендера, изображения - нет. Nginx, h2o и Envoy соблюдают веса приоритетов. На практике это значит, что gRPC bidirectional streaming (health checks Kubernetes) и обычные HTTP-ответы сосуществуют на одном соединении без голодания.
HTTP/2 решает HOL blocking на уровне **приложения**, но не TCP. Один потерянный TCP-пакет останавливает все streams - это HOL blocking транспортного уровня. HTTP/3 устраняет и его.
Почему HTTP/1.1 требует несколько TCP-соединений для параллельных запросов?
HPACK и Server Push
HTTP/1.1 отправляет заголовки текстом при каждом запросе. `User-Agent`, `Accept`, `Cookie`, `Authorization` - одинаковые строки снова и снова. Сотни байт на каждый запрос. На мобильных сетях с высоким RTT это заметный overhead при загрузке страницы с 80+ ресурсами.
**HPACK** сжимает заголовки HTTP/2 через две таблицы: статическую (61 предопределённая пара типа `:method GET`) и динамическую (пополняется в ходе соединения). Повторный заголовок передаётся как 1-байтный индекс вместо полной строки. Инженеры Google измерили: HPACK сокращает объём заголовков на 85-88% для типичных веб-запросов.
HPACK: первый vs повторный запрос
Первый запрос: полная сериализация всех заголовков (~400 байт). Второй запрос к тому же хосту: только изменившиеся заголовки + 1-байтные индексы для остальных (~50 байт). Экономия: 87.5% трафика заголовков на странице с 80+ ресурсами.
**Server Push** - превентивная отправка ресурсов. Сервер видит запрос к `index.html` и без дополнительных запросов клиента отправляет `styles.css` и `app.js` в отдельных streams. Теоретически элегантно. Практически - браузеры научились игнорировать push-фреймы для уже закешированных ресурсов, Chrome отключил Server Push по умолчанию в 2022 из-за edge cases с кешированием.
Server Push - одна из наиболее переоценённых фич HTTP/2. Правильная реализация требует точного знания кеш-состояния клиента. Большинство команд отключают его и используют `<link rel=preload>` вместо этого.
HPACK и мультиплексирование активно используются в современных системах: Kubernetes API-сервер отдаёт watch-стримы (`kubectl get pods -w`) по HTTP/2 streams, Envoy и Nginx реализуют HTTP/2 connection pooling в upstream-запросах. Весь gRPC трафик идёт через эти механизмы.
Как HPACK добивается сжатия HTTP/2 заголовков?
HTTP/3 и QUIC: UDP как новый TCP
HTTP/2 решил HOL blocking на уровне приложения - но оставил его на уровне TCP. Один потерянный пакет блокирует все 100 параллельных HTTP/2 streams, пока TCP не восстановит его. На мобильных сетях с packet loss 1-2% это чувствительно. Google снова взялся за инструменты.
**QUIC** (RFC 9000, 2021) - транспортный протокол поверх UDP. Реализует всё что делает TCP: надёжная доставка, управление перегрузкой, flow control - но как пользовательский код, а не в ядре ОС. Ключевое следствие: новые алгоритмы (BBR v3, CUBIC++) деплоятся как обновление приложения без ожидания обновления ядра на миллиардах устройств.
**0-RTT handshake** - ключевое преимущество для повторных соединений. TLS 1.3 поверх TCP требует 1-RTT handshake. QUIC с 0-RTT отправляет данные первым пакетом при повторном соединении - клиент использует ключи от предыдущей сессии. Измерение Google: медианная латентность открытия страницы снижается на 8% только за счёт 0-RTT.
**Connection migration** меняет мобильный UX. TCP-соединение привязано к четырёхтуплу (src IP, dst IP, src port, dst port). Переключение с WiFi на LTE = новый IP = разрыв всех соединений. QUIC использует **Connection ID** вместо четырёхтупла. При смене IP клиент отправляет пакет с тем же Connection ID - сервер продолжает без переподключения. YouTube на телефоне не буферизуется при выходе из зоны WiFi именно поэтому.
HOL blocking транспортного уровня устранён: в QUIC каждый stream независим. Потеря пакета stream 5 не влияет на streams 1, 3, 7. Cloudflare измерила: на сетях с 2% packet loss HTTP/3 быстрее HTTP/2 на 12%, на 10% потерь - на 37%.
По данным W3Techs (2024), HTTP/3 поддерживает около 30% веб-сайтов. Cloudflare, Google, Meta включили его по умолчанию. nginx поддерживает через quiche. Есть нюанс: корпоративные firewall'ы часто блокируют UDP - QUIC предусматривает автоматический TCP fallback, если QUIC не проходит за 300ms.
HTTP/3 ненадёжен, потому что UDP не гарантирует доставку
QUIC реализует надёжность поверх UDP самостоятельно
UDP - просто низкоуровневый транспорт без встроенной логики. QUIC добавляет ACK, повторную передачу, управление перегрузкой и flow control - всё как в TCP, но без ограничений ядра ОС.
Почему HTTP/3 построен поверх UDP, а не поверх TCP?
Связанные концепции
HTTP/2 и HTTP/3 - транспортная основа для gRPC, WebSocket и современных CDN.
- gRPC и HTTP/2 — gRPC использует HTTP/2 streams как транспортный уровень для bidirectional streaming
- TLS 1.3 handshake — 0-RTT в QUIC строится поверх механизма session resumption TLS 1.3
- WebSocket над HTTP/2 — RFC 8441 расширяет WebSocket для работы через HTTP/2 streams вместо upgrade-соединений
Итог
- HTTP/2: одно TCP-соединение, множество параллельных streams с приоритетами - ликвидирует HOL blocking на уровне приложения
- HPACK сжимает повторяющиеся заголовки до 1-байтных индексов - экономия 85-88% трафика заголовков
- Server Push элегантен в теории, проблематичен на практике - Chrome отключил по умолчанию в 2022
- HTTP/3 поверх QUIC (UDP): независимые streams без TCP HOL, 0-RTT handshake, connection migration при смене IP
- QUIC реализует TCP-надёжность на уровне приложения - новые алгоритмы без обновления ядра ОС
Вопросы для размышления
- В чём принципиальное отличие HOL blocking в HTTP/2 и HTTP/3? На каком уровне каждый из них решает проблему?
- Почему Server Push в HTTP/2 оказался менее полезным на практике, чем ожидалось при проектировании?
- Как Connection migration в QUIC меняет UX для мобильных приложений с постоянными соединениями?
Связанные уроки
- bt-05-http-fundamentals — HTTP/1.1 head-of-line - отправная точка проблемы
- bt-04-dns-tls — TLS 1.3 и 0-RTT handshake прямо в QUIC
- bt-08-websocket — WebSocket над HTTP/2 - полнодуплекс через RFC 8441
- bt-09-grpc — gRPC использует HTTP/2 streams как транспорт
- net-24-http2-http3 — Тот же материал с сетевой перспективы (OSI L7)