1. 1
  2. 2
  3. 3
  4. 4
  5. 5

# теория · шаг 1 из 5

Что HTTP/2 меняет на самом деле

Коротко

HTTP/2 - это мультиплексирование в одном соединении плюс сжатие заголовков. Он снимает лимит на шесть соединений к домену.

  • Выигрыш появляется не всегда: на чистой сети замер даёт 814 мс против 821 у шести соединений HTTP/1.1 - то есть поровну.
  • На сети с потерями всё решает, где теряются пакеты. На пути запросов - HTTP/2 выигрывает, 813 мс против 1146. На пути данных - проигрывает, медиана 2021 мс против 1237: потерянный сегмент останавливает все потоки единственного соединения, а у шести соединений HTTP/1.1 - только одно из них.

Если знаешь, что мультиплексирование не отменяет очередь в TCP, - листай до «Теперь сам».

Сначала ответь сам

Страница тянет 30 файлов. Включаем HTTP/2 и ничего больше не трогаем. Насколько быстрее станет?

Стенд, задержка 100 мс, семь прогонов, медиана:

↳  вывод30 файлов по 20 КБ, сеть без потерь
HTTP/1.1, шесть соединений   821 мс
HTTP/2,   одно соединение    814 мс

Поровну. Мультиплексирование не ускоряет само по себе: шести соединений хватает, чтобы вытянуть тридцать файлов за то же время. Выигрыш HTTP/2 в другом - и проявится он на плохой сети, а не на хорошей.

На что это похоже

Шесть кассиров против одного конвейера. Пока покупателей тридцать, а очередь движется ровно, разницы почти нет: шесть касс справляются.

Разница появляется, когда у кого-то заминка. У шести касс встанет одна из шести - остальные пять работают. У конвейера заминка в начале останавливает всё, что за ней.

Так вот HTTP/2 - это конвейер. И именно поэтому дальше в главе речь пойдёт о том, чего он не чинит.

Механизм: фреймы вместо очереди

HTTP/1.1: шесть соединений, в каждом по очереди запрос 1 запрос 7 запрос 13 остальные 24 ждут своей очереди в шести трубах HTTP/2: одно соединение, фреймы вперемешку 1 7 2 13 7 1 30 2 13 30 каждый фрейм помечен номером потока, на другом конце их разбирают ниже всё равно один TCP - и это то, что ломается на потерях
Внутри протокола потоки независимы. Ниже протокола - один поток байтов.

Второе, что даёт HTTP/2, - сжатие заголовков. В HTTP/1.1 каждый из тридцати запросов тащит одинаковые User-Agent, Accept, Cookie: легко килобайт на запрос. HTTP/2 передаёт повторы ссылкой на таблицу, которую обе стороны ведут синхронно.

Правило

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

Браузер держит к одному домену не больше шести одновременных соединений - отсюда и костыли, придуманные, чтобы обойти этот предел:

  • доменный шардинг (img1.shop.ru, img2.shop.ru) - каждый лишний домен это отдельное TLS-рукопожатие и отдельная таблица заголовков;
  • спрайты (много мелких картинок, собранных в одну) и склейка бандлов (весь JavaScript в один файл) - правка одной строки инвалидирует в кеше весь склеенный файл. Но и дробить до файла на модуль не стоит: у каждого запроса остаются заголовки и обработка, а мелкие файлы хуже сжимаются. Обычно выигрывают несколько осмысленных кусков - общий код отдельно, страницы отдельно, - и выбирают их по замеру;
  • инлайн base64-картинок в CSS - раздувает файл, который иначе лежал бы в кеше отдельно, и плохо сжимается.

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

Разбор: где HTTP/2 начинает выигрывать

Тот же стенд, но с потерями на пути запросов:

↳  вывод30 файлов, задержка 100 мс, потери 5%
HTTP/1.1, шесть соединений   1146 мс
HTTP/2,   одно соединение     813 мс

Вот теперь разница есть, и взялась она не из мультиплексирования как такового. У шести соединений каждое ведёт свой контроль перегрузки (механизм TCP, который сам замедляет отправку, нащупывая скорость канала) и своё восстановление после потерь; потерянный пакет тормозит одно из шести, но соединений всего шесть, а файлов тридцать - и очередь наверстать не успевает.

Разбор: чего HTTP/2 не чинит

Теперь потери переносим на сторону сервера - туда, где идут сами данные. Семь прогонов, отсортированные времена:

протокол прогоны, мс
HTTP/1.1, шесть соединений 1017 1120 1214 1237 1341 1420 1767
HTTP/2, одно соединение 1313 1413 1714 2021 5221 5268 5486

Медиана HTTP/2 хуже почти вдвое, а хвост уходит за пять секунд. Причина - TCP: он отдаёт данные приложению строго по порядку, и пока потерянный сегмент не доехал повторно, ядро держит всё, что пришло после него. Встают все тридцать потоков разом, включая те, чьи данные уже лежат в буфере.

Это head-of-line blocking на уровне транспорта. Внутри TCP его не починить - очерёдность часть его контракта. Отсюда и вырос QUICРазбирается в разделе 10, глава «HTTP/3 и QUIC»: Почему понадобился QUIC, следующая глава.

Что ломается без этого

Server push умер. Идея «отправим файлы до того, как их попросят» не пережила столкновение с кешем браузера: сервер не знает, что у клиента уже есть, и push регулярно слал байты в никуда. Chrome выключил поддержку в 2022 году, а nginx отвечает на директиву прямо:

↳  выводnginx -t
[warn] the "http2_push" directive is obsolete, ignored

Замена - 103 Early Hints: бэкенд, зная, что ответ будет долгим, заранее отдаёт промежуточный ответ со ссылками, а браузер сам решает, что предзагрузить. nginx проксирует такие ответы с версии 1.29.0 (в стабильной ветке с 1.30.0):

/etc/nginx/conf.d/shop.local.confhttp › server › location /
early_hints on;

Приоритеты потоков практически не используются. Сложная схема зависимостей из первой редакции протокола оказалась неработоспособной и была выкинута; пришедшая ей на смену поддержана далеко не везде. Считай, что порядок отдачи решает браузер.

Зачем это в работе

Из замеров следует практический вывод, который редко пишут в статьях: включение HTTP/2 само по себе почти ничего не даёт на хорошей сети. Оно даёт две вещи: устойчивость к плохой сети до определённого предела и возможность разобрать костыли.

Поэтому оценивать эффект надо не «стало ли быстрее у меня в офисе», а по метрикам с реальных клиентов - и отдельно смотреть на мобильных, где потери есть всегда.

Теперь сам

Сайт перевели на HTTP/2, разобрали шардинг и разбили бандл на двадцать файлов. Метрики у десктопных клиентов не изменились, у мобильных стало хуже. Что произошло?

Ровно то, что на графике замера: у мобильных потери, и одно соединение с двадцатью потоками страдает от head-of-line blocking сильнее, чем прежние шесть. Разбиение бандла усилило эффект - файлов стало больше, и каждая потеря задерживает их все. Лечится это не возвратом к одному бандлу и не двадцатью файлами, а несколькими кусками покрупнее и замером на мобильных клиентах. HTTP/3 рядом с HTTP/2 убирает саму причину - очередь TCP, - но и его меряют: на стенде следующей главы конкретная сборка проиграла обоим.

Главное

HTTP/2 объединяет потоки в одном TCP-соединении, поэтому потери на этом соединении могут задержать сразу несколько ответов. Проверяй эффект на типичных клиентах и сети. Результат одного стенда не определяет универсальный способ разбиения ресурсов или гарантированный выигрыш от смены протокола.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.