awesome-everything EN
↑ Обратно к восхождению

Сети и протоколы

HTTP/2: потоки, фреймы и HPACK

Суть HTTP/2 заменил обходной путь HTTP/1.1 с параллельными соединениями на мультиплексированные потоки на одном TCP — HPACK сжимает заголовки, Server Push оказался тупиком.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 14 min

Браузер загружает страницу с 50 подресурсами: открывает 6 TCP-соединений к одному origin, заполняет их запросами, ставит остальные в очередь. Каждое соединение платит свой хендшейк. HTTP/2 сворачивает всё это в одно соединение с 50 параллельными потоками — но у исправления есть собственный режим отказа.

HTTP/1.1: pipelining и шесть соединений

HTTP/1.1 (RFC 9112) отправляет один запрос на TCP-соединение и ждёт полного ответа перед следующим. Для параллелизации браузеры открывают 6–8 соединений к одному origin. При загрузке 10 JS-файлов открывается 6 соединений, по одному запросу на каждое, остальные 4 в очереди. Когда ответ приходит — соединение освобождается и берёт следующий файл.

Pipelining (RFC 7230) позволял отправлять несколько запросов не дожидаясь ответов — но редко работал: прокси ломали его, и если запрос #1 ждал медленного сервера, запрос #2 блокировался за ним на том же соединении — HOL-блокировка на стороне ответа. Почти все браузеры отключили pipelining по умолчанию.

HTTP/2: слой фреймирования

HTTP/2 (RFC 9113) отказывается от модели одного запроса на соединение. Одно TCP-соединение несёт много параллельных потоков — каждый поток независимая пара запрос-ответ.

Фреймы — единица передачи:

  • HEADERS — открывает поток, несёт метаданные запроса или ответа. Поток создаётся когда клиент отправляет HEADERS с новым stream ID.
  • DATA — несёт тело потока.
  • RST_STREAM — прерывает поток без закрытия соединения.
  • SETTINGS — согласует параметры соединения (максимум потоков, начальный размер окна, размер таблицы заголовков).
  • WINDOW_UPDATE — управление потоком: сообщает отправителю сколько байт получатель готов буферизировать.

Сервер может перемежать ответы разных потоков: 100 байт ответа потока #1, затем 100 байт потока #3, затем снова #1 — всё на одном TCP. Порядок внутри одного потока сохраняется; порядок между потоками — на усмотрение сервера.

Факты о фреймировании HTTP/2
Размер заголовка фрейма HTTP/2
9 байт (тип, флаги, длина, stream ID)
Пространство stream ID
31 бит (нечётные — клиент, чётные — server push)
Окно управления потоком по умолчанию
65 535 байт на поток
Типичный сервер: макс. параллельных потоков
100 (рекомендация RFC 9113)
SETTINGS фрейм согласуется
при старте соединения, до первого запроса
Стоимость RST_STREAM
один фрейм — без разрыва TCP

HPACK: сжатие заголовков

HTTP/1.1 отправляет полные заголовки в каждом запросе — обычно 400–800 байт даже если меняется только путь. HTTP/2 использует HPACK (RFC 7541):

  1. Статическая таблица — 61 предопределённая пара (имя, значение): ":method": "GET", ":status": "200", "content-type": "text/html" и т.д. Один байт индекса заменяет полный заголовок.
  2. Динамическая таблица — записи добавляются per-connection по мере появления новых заголовков. Если первый запрос отправляет user-agent: Mozilla/5.0, второй может ссылаться на него 1–2 байтами индекса, передавая только дельту.

Результат: после первого запроса к домену, последующие запросы уменьшают накладные расходы на заголовки с ~400 байт до ~20–50 байт. На странице с 50 подресурсами это экономит ~18 КБ заголовочных данных.

Ограничение: динамическая таблица упорядочена и общая для всех потоков. Если HEADERS-фрейм, добавляющий индекс 60, потерян, последующие HEADERS не могут декодировать ссылки на индекс 60 до повторной доставки. На TCP это незаметно. На QUIC это сломает независимость потоков — поэтому HTTP/3 заменил HPACK на QPACK.

Управление потоком HTTP/2

Каждое HTTP/2-соединение имеет connection-level и stream-level управление потоком через WINDOW_UPDATE — получатель сообщает отправителю сколько байт готов принять. По умолчанию: 65 535 байт на поток, иногда повышается до 16 МиБ на высокопропускных API.

SETTINGS_MAX_CONCURRENT_STREAMS ограничивает число параллельных потоков (обычно 100). При достижении лимита клиент ставит запросы в очередь — частично теряя выгоду мультиплексирования.

Проследи
1/5

Трассировка загрузки страницы браузером по HTTP/2 с нуля.

1
Step 1 of 5
Браузер завершает DNS, TCP, TLS. Что он анонсирует в ALPN?
2
Locked
TLS-хендшейк завершён. Что браузер отправляет первым?
3
Locked
Сервер отвечает. Как выглядит ответ на уровне фреймирования?
4
Locked
Браузер разбирает HTML и обнаруживает 50 подресурсов. Что он делает?
5
Locked
Один TCP-пакет теряется. Что происходит со всеми 50 потоками?

Server Push: функция, которая провалилась

HTTP/2 ввёл Server Push — сервер мог отправлять ресурсы которые браузер ещё не запрашивал. Получив GET /index.html, сервер мог послать PUSH_PROMISE для /style.css, предполагая что он понадобится.

В теории — экономия одного round-trip. На практике — три причины провала:

  1. Сервер не знает что браузер уже закешировал. Слепо пушит /style.css даже если у браузера свежая копия, тратя трафик.
  2. Push помогает только при первой навигации — при тёплых загрузках браузер агрессивно кеширует и пушнутые ресурсы приходят незваными.
  3. Middleboxes и CDN не обрабатывали push корректно, приводя к дублированным передачам.

Chrome убрал Server Push в 2022. RFC 9113 депрекейтит PUSH_PROMISE. Современная замена: 103 Early Hints + <link rel="preload"> — сервер отправляет 103 информационный ответ с критическими подресурсами, браузер сам решает что загружать (проверяя кеш первым).

Викторина

Почему Server Push фактически мёртв в 2026?

Викторина

HTTP/2 мультиплексирование устраняет HOL-блокировку — верно или нет?

Расставь шаги по порядку

Расставьте события при загрузке https://example.com по HTTP/2:

  1. 1 DNS-резолвинг example.com
  2. 2 TCP-хендшейк
  3. 3 TLS 1.3 хендшейк (ALPN выбирает h2)
  4. 4 Обмен SETTINGS-фреймами HTTP/2
  5. 5 HEADERS-фрейм с запросом страницы
  6. 6 DATA-фреймы с HTML-телом
  7. 7 50 HEADERS-фреймов для подресурсов
  8. 8 Перемежающиеся DATA-фреймы всех 50 потоков
Проследи
1/4

Диагностика: почему HTTP/2 API-сервис имеет большую задержку на мобильных сетях, чем HTTP/1.1?

1
Step 1 of 4
Шаг 1: какая метрика сети сразу скажет есть ли потеря пакетов?
2
Locked
Шаг 2: 1.2% потерь на мобильном пути. API делает 50 параллельных запросов на страницу. Что происходит в HTTP/2?
3
Locked
Шаг 3: тот же сценарий по HTTP/1.1 с 6 параллельными соединениями. Что происходит?
4
Locked
Шаг 4: стоит ли откатиться к HTTP/1.1 на мобиле?
Вспомните перед уходом
  1. 01
    Объясните почему HTTP/2 мультиплексирование НЕ решает полностью HOL-блокировку.
  2. 02
    Что такое HPACK и каково его главное ограничение?
  3. 03
    Что заменило Server Push HTTP/2 и почему это работает лучше?
Итог

HTTP/2 ввёл мультиплексирование потоков на одном TCP-соединении, заменив шесть-соединений HTTP/1.1. Фреймы (HEADERS, DATA, RST_STREAM, SETTINGS, WINDOW_UPDATE) — единица передачи; потоки свободно чередуются на одном соединении. HPACK сокращает размер заголовков на 80–90% через статическую и per-connection динамическую таблицу. Управление потоком через WINDOW_UPDATE предотвращает переполнение буферов. Server Push удалён из Chrome в 2022 — сервер не знает что браузер закешировал; 103 Early Hints заменил его. Оставшаяся открытая проблема: один потерянный TCP-пакет тормозит каждый поток — HOL-блокировка TCP, которую HTTP/3 решает с QUIC.

Связанные уроки
встречается в5
Продолжить восхождение ↑HTTP/3 и QUIC: изоляция потерь на уровне потока
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.