Изображение взято с сайта www.aerotime.aero


Всем привет, меня зовут Семён, я руковожу разработкой партнёрских сервисов в ДомКлике. Недавно, работая над оптимизацией загрузки страниц, я наткнулся на интересную заметку от Cloudflare о приоритизации потоков при работе с CDN. Я заинтересовался и решил проверить, поддерживает ли наш CDN эту опцию стандарта HTTP/2? Тех, кому интересно узнать больше, прошу под кат, где мы рассмотрим механизм приоритизации HTTP/2-потоков и протестируем работу некоторых CDN.


Немного теории. HTTP/2


Раз уж мы заговорили про HTTP/2, давайте вспомним, что нового нам предложил этот протокол:


  • HTTP/2 бинарный, не текстовый.
  • Мультиплексированные потоки (возможность параллельно пересылать фреймы из многих потоков, используя одно TCP-соединение).
  • Сжатие заголовков.
  • Сервер пуш.
  • Расширение Application-Layer Protocol Negotiation (ALPN) для быстрой работы по TLS.
  • Отпала необходимость шардировать домены со статикой (практика создания нескольких доменов, например, static[1-3], призванная обойти ограничения браузеров по количеству параллельных подключений к одному хосту).

Среди прочего, HTTP/2 позволяет приоритизировать потоки при мультиплексировании.


Как работает приоритизация потоков в HTTP/2


Каждому потоку при мультиплексировании:


  • можно присвоить вес — целочисленное значение от 1 до 256;
  • можно присвоить номер потока, от которого он зависит.

Комбинация веса и зависимостей потоков позволяет клиенту (браузеру) построить «дерево приоритизации» для описания порядка получения ответов. В свою очередь, сервер может использовать эту информацию для выделения ресурсов приоритетным ответам.


Важно заметить, что использование механизмов приоритизации потоков клиентом не гарантирует определённого порядка обработки и пересылки сервером.

«Родительские» потоки (от которых зависят другие потоки) получают ресурсы первыми. Если у двух «потомков» одинаковый родитель, потомки получают ресурсы пропорционально значению weight в заголовке HTTP/2 Stream.


Например, есть два потока, которые запускаются в рамках одного TCP-соединения:


HTTP/2 Stream: 57, weight 15, depends on 55, EXCLUSIVE

HTTP/2 Stream: 63, weight 5, depends on 55, EXCLUSIVE

Оба потока зависят от потока №55, поэтому он должен первым получить ресурсы сервера (CPU, memory, output buffers) на выполнение. Далее ресурсы должны быть распределены между потоками 57 и 63 согласно весам: 57 должен получить 15/20, а 63 — 5/20, т.е. поток 57 должен получить в три раза больше ресурсов.


Почему вообще это важно?


При использовании HTTP/2 клиент открывает одно соединение к серверу. По этому соединению загружается вся статика: CSS, JS, изображения, шрифты и т.д. В каком порядке их загружать? А может, всё вместе параллельно?


Загружать все артефакты одновременно — плохо, так как это увеличивает общее время загрузки страницы и зачастую не имеет смысла для пользователя на первом экране. Скажем, у нас есть страница, на которой отображается тридцать изображений, но на первом экране пользователь видит только пять из них. В этом случае лучше загрузить первые пять изображений, а остальные после того, как пользователь увидит первый экран. Или, например, есть JS-скрипты, которые загружаются с async или defer — их тоже можно оставить на потом. Т.е. сначала необходимо использовать всю ширину канала для загрузки артефактов, которые помогут пользователю увидеть первый экран (CSS, JS, шрифты), и уже потом загружать менее важные вещи.


А что же CDN?


С помощью механизма приоритизации потоков браузеры могут сообщать серверу о предпочитаемом порядке получения ресурсов. Однако оказывается, что не все серверы используют этот механизм для аллокации ресурсов.


Патрик Минан из Google создал простой тест для проверки поддержки сервером стандарта HTTP/2 Stream prioritization.


Тест


Тест выполняется с помощью специального ресурса на https://www.webpagetest.org.


Необходимо задать параметры эксперимента:


  • Выбрать картинку размером около 100 Кб на проверяемом ресурсе;.
  • Web Site URL: указать https://www.webpagetest.org/http2priorities.html с параметром image из предыдущего шага.
  • Browser выбрать Chrome.
  • Location не так важно, но логично выбрать ближе к edge location вашей CDN.
  • Connection лучше указать помедленнее, "3G Fast" (1,6 Мбит/с. / 768 Кбит/с., 150 мс RTT).
  • Runs указать побольше, до девяти.

Тест для проверки CDN написан разработчиком Chrome, поэтому реализует логику приоритизации, используемую в этом браузере.

После запуска тест выполнит следующие шаги:


  • Загрузит HTML документ (запрос №1).
  • Дважды загрузит указанную в тесте картинку (запросы №2 и №3).
  • Установит и «прогреет» TCP-соединение с сервером.
  • Добавит уникальный хэш к каждому из запросов, чтобы убедиться, что изображения будут доставлены по сети, а не из кэша.
  • Запросит изображение с сервера 30 раз с низким приоритетом (запросы №4-33).
  • Подождёт, пока два низкоприоритетных изображения загрузятся, чтобы дать серверу возможность заполнить буферы данными.
  • Запросит изображение с высоким приоритетом и дождётся полной загрузки (запрос №34).
  • Запросит изображение с высоким приоритетом (запрос №35).

Результаты теста


CDN, который используем мы (внешний подрядчик)



Как видно из графика, наш CDN обратил внимание на приоритет потоков, перераспределил ресурсы и загрузил изображения с высоким приоритетом в сравнимое с эталонным время, гораздо раньше низкоприоритетных.


Cloudfront


Я решил сравнить результаты теста с каким-нибудь известным CDN и взял для этого Cloudfront от AWS. Результат меня удивил:



Как оказалось, Cloudfront не учитывает приоритеты потоков: время выполнения первого приоритетного запроса было на порядок выше эталонного, а второй приоритетный запрос и вовсе закончился позже многих низкоприоритетных.


Заключение


Мы рассмотрели механизм приоритизации потоков в HTTP/2 и протестировали работу некоторых CDN. Выяснилось, что не все CDN, даже самые крупные и известные, поддерживают эту опцию стандарта HTTP/2.


Ссылки


Стандарт HTTP/2 RFC7540
Книга O'Reilly High Performance Browser Networking
Репозиторий с тестом CDN by Patrick Meenan
Статья Clouldflare о Stream prioritization