Изображение взято с сайта 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
Автор: Семён Багреев