HTTP/1.1 держит соединение и страдает очередью запросов на одном TCP. HTTP/2 мультиплексирует стримы в одном соединении. Head-of-line на уровне TCP при потере пакета остаётся до HTTP/3.
Транспорт и DNS http собеседование
HTTP/1.0 обычно закрывал соединение после ответа. HTTP/1.1 сделал persistent connection нормой: на одном TCP можно отправить несколько запросов подряд. Но семантика всё ещё очередь: прочитал запрос целиком — пишешь ответ.
Head-of-line (HOL) простыми словами: быстрый запрос стоит в очереди за медленным. Даже если ваш запрос крошечный. Одна труба — одна очередь.
Пример. Касса в магазине. Перед вами человек с полной тележкой. Вы с одной бутылкой воды ждёте. Это HOL: быстрый запрос застрял за медленным, потому что очередь одна.
Браузеры лечили очередь пулом соединений. Исторически около шести на хост. Плюс domain sharding. На сервере один медленный ответ на keep-alive соединении блокирует следующие запросы клиента на этой трубе. Это HOL уже на уровне HTTP.
Pipelining против multiplex
Pipelining в HTTP/1.1 разрешал отправить несколько запросов подряд, не дожидаясь ответов. На практике его почти не используют. Прокси путают порядок. Один медленный ответ блокирует очередь.
HTTP/2 переводит диалог в бинарные фреймы. Много стримов, каждый со своим id, в одном TCP. Медленный стрим не обязан останавливать другие на уровне HTTP. HPACK сжимает заголовки.
Почти везде HTTP/2 требует TLS и ALPN. Cleartext h2c бывает внутри service mesh, не в браузере. Multiplex — не «несколько TCP на один запрос» и не UDP. Это несколько стримов в одном TCP.
// HTTP/1.1 keep-alive, без pipelining:
// req1 ------------------> (медленно)
// req2 ждёт окончания req1 на этом conn
// лечение: пул соединений / новый TCP
//
// HTTP/1.1 pipelining (редко в проде):
// req1, req2, req3 ------> ответы строго по порядку → HOL
//
// HTTP/2:
// stream 1 GET /slow
// stream 3 GET /fast // фреймы перемешаны в одном TCP
// потеря TCP-сегмента → все стримы ждут retransmission
// HTTP/3/QUIC: стримы на UDP, потеря одного не блокирует остальные
Один медленный ответ в HTTP/1.1 стопит очередь. В HTTP/2 стримы делят TCP, но потеря пакета стопит все стримы.
Head-of-line на двух этажах
- HTTP/1.1: один медленный ответ на соединении блокирует следующие запросы. Лечили пулом TCP.
- HTTP/2: HOL на TCP — потерянный пакет останавливает все стримы этого соединения.
- HTTP/3 уносит стримы на QUIC/UDP: потеря одного не блокирует остальные.
Пример. В HTTP/2 касс несколько (стримы), но все товары едут на одном грузовике (TCP). Грузовик потерял коробку — все кассы ждут, пока коробку привезут заново. HTTP/3 — у каждой кассы свой маленький грузовик на UDP.
Не говорите «HTTP/2 просто быстрее». Выигрыш — multiplex, дешевле handshake на много ресурсов, сжатие заголовков. На стабильной сети с одним тяжёлым API выигрыш может быть скромным. Версию для публичного API часто выбирает CDN и балансировщик, а не ваш хендлер в Go.
Что из этого в Go
Сервер http.Server с TLS включает HTTP/2 по умолчанию. Клиент тоже. Для h2c нужны отдельные настройки. gRPC поверх HTTP/2 наследует и multiplex, и TCP HOL: много тяжёлых RPC на одном соединении всё ещё делят судьбу потерянного пакета.
Типичная ошибка
Считать, что HTTP/2 полностью убрал head-of-line. На уровне фреймов HTTP — да, на уровне TCP — нет. Путать pipelining 1.1 с multiplex 2: первое на практике мертво из‑за HOL и кривых прокси.
На собеседовании
Скажите: multiplex и HPACK у HTTP/2, HOL на TCP до HTTP/3. Pipelining не используют из‑за блокировки очереди. Для публичного API версию часто выбирает край (CDN/LB), не хендлер.