Перейти к содержимому
34 / 41

Что такое Head-of-Line Blocking и как HTTP/2 и HTTP/3 его решают?


Полный ответ

HOL Blocking существует на двух уровнях:

JavaScript
Уровень 1: HTTP (прикладной)

HTTP/1.1 -- запросы в одном соединении обрабатываются последовательно:

Запрос 1: [---- ответ (медленный SQL) ------]
Запрос 2:                                     [-- ответ --]  (ждал)
Запрос 3:                                                    [- ответ -]  (ждал ещё дольше)

Запрос 2 и 3 были готовы на сервере, но ждали завершения ответа 1

HTTP/2 решает: мультиплексирование, ответы чередуются фреймами:

Stream 1: [frame]      [frame]            [frame]     (медленный)
Stream 2:    [frame][frame]                            (быстрый, не ждёт)
Stream 3:              [frame][frame]                  (быстрый, не ждёт)

JavaScript
Уровень 2: TCP (транспортный)

TCP гарантирует порядок доставки байтов.
Если пакет потерян, ВСЕ последующие пакеты ждут ретрансмиссии.

HTTP/2 -- все потоки в одном TCP-соединении:

TCP пакеты: [1: stream A][2: stream B][3: stream A][4: stream C]

Если пакет 2 потерян:
Пакеты 3 и 4 получены, но TCP не отдаёт их приложению
Все потоки (A, B, C) заблокированы из-за одного потерянного пакета

HTTP/3 (QUIC) -- каждый поток независим на транспортном уровне:

QUIC поток A: [пакет 1][пакет 3]  -- свой порядок
QUIC поток B: [пакет 2 потерян]   -- ждёт ретрансмиссию
QUIC поток C: [пакет 4]           -- не заблокирован!

Потеря пакета потока B не влияет на потоки A и C

Визуализация трёх протоколов:

JavaScript
HTTP/1.1:
Соединение 1: [====запрос A====][====запрос B====]   HOL на HTTP-уровне
Соединение 2: [====запрос C====][====запрос D====]   Обход: больше соединений
(6-8 параллельных соединений -- костыль)

HTTP/2:
TCP соединение: [A-frame][B-frame][C-frame][A-frame]  Нет HTTP HOL
Но при потере TCP-пакета:
TCP: [...ждём ретрансмиссию...][все фреймы разблокированы]  TCP HOL

HTTP/3 (QUIC):
QUIC поток A: [данные]     [данные]       Независимый
QUIC поток B: [потеря...ждём]             Только B заблокирован
QUIC поток C: [данные][данные]            Не ждёт B
Нет HOL ни на каком уровне

Дополнительные преимущества HTTP/3:

JavaScript
1. 0-RTT connection establishment
HTTP/1.1 + TLS 1.2:  TCP (1 RTT) + TLS (2 RTT)  = 3 RTT до первых данных
HTTP/2   + TLS 1.3:  TCP (1 RTT) + TLS (1 RTT)  = 2 RTT
HTTP/3   + QUIC:     QUIC (1 RTT, включает TLS)  = 1 RTT
HTTP/3   + 0-RTT:    QUIC с кешированным ключом   = 0 RTT (мгновенно!)

2. Connection migration
TCP: привязан к IP:port. Переключил WiFi -- новое соединение, заново TCP+TLS
QUIC: привязан к Connection ID. Переключил WiFi -- соединение продолжается

3. Встроенное шифрование
QUIC всегда шифрован, нет незащищённого варианта

Проверка протокола:

JavaScript
// Определение протокола в браузере
const [nav] = performance.getEntriesByType('navigation');
console.log(nav.nextHopProtocol);
// "http/1.1", "h2", "h3"

// Для всех ресурсов
const resources = performance.getEntriesByType('resource');
const protocols = new Map();

for (const res of resources) {
  const proto = res.nextHopProtocol || 'unknown';
  protocols.set(proto, (protocols.get(proto) || 0) + 1);
}

console.log('Протоколы загрузки:');
for (const [proto, count] of protocols) {
  console.log(`  ${proto}: ${count} ресурсов`);
}

Настройка серверов:

Nginx

# nginx с HTTP/2
server {
    listen 443 ssl http2;
    ssl_certificate     /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;
}

# nginx с HTTP/3 (начиная с 1.25.0)
server {
    listen 443 ssl http2;
    listen 443 quic;          # HTTP/3 через QUIC

    ssl_certificate     /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;

    # Заголовок, сообщающий браузеру о поддержке HTTP/3
    add_header Alt-Svc 'h3=":443"; ma=86400';
}

📌 Реальные кейсы

Когда TCP HOL Blocking особенно заметен:

JavaScript
Сценарий: мобильная сеть с 2% потерь пакетов

HTTP/2 через TCP:
Каждая потеря блокирует ВСЕ потоки
При 50 параллельных потоках одна потеря останавливает 50 запросов
На нестабильном мобильном интернете это критично

HTTP/3 через QUIC:
Потеря в одном потоке не трогает остальные 49
Страница продолжает грузиться
Разница в реальной скорости: 10-30% на плохих сетях

Как браузер переходит на HTTP/3:

JavaScript
1. Первый запрос идёт по HTTP/2 (TCP)
2. Сервер отвечает с заголовком: Alt-Svc: h3=":443"; ma=86400
3. Браузер запоминает: этот домен поддерживает HTTP/3
4. Следующие запросы идут по QUIC
5. Если QUIC не работает (файрвол блокирует UDP), fallback на HTTP/2

Процесс прозрачен для разработчика -- код не меняется

Резюме

HOL Blocking -- это когда один медленный или потерянный элемент задерживает все остальные. HTTP/1.1 страдал от HTTP-уровня HOL (один медленный ответ блокирует очередь). HTTP/2 решил это мультиплексированием, но получил TCP HOL (потеря пакета блокирует все потоки). HTTP/3 (QUIC поверх UDP) устраняет оба уровня блокировки. Для разработчика переход прозрачен -- достаточно настроить сервер.

Что такое Head-of-Line Blocking и как HTTP/2 и HTTP/3 его решают? | JScriptiser