Сайты по-прежнему загружаются слишком медленно. В самый критический момент процесса загрузки канал зачастую практически полностью простаивает. Новая технология, предложенная инженером Кадзухо Оку из Fastly, поможет лучше использовать эту критическую первую пару секунд.
Вы когда-нибудь загружали сайт на телефоне — и смотрели в течение 10 секунд на страницу без текста? Никто не любит сидеть и глядеть на пустой экран, пока загружается какой-то необычный шрифт. Поэтому есть смысл перенести загрузку таких важных вещей на максимально раннее время. Предзагрузка Link rel=preload должна была частично решить проблему. Первым делом браузер парсит HTTP-заголовки, так что это идеальное место, чтобы указать на предварительную загрузку ресурса, который точно понадобится позже.
Давайте посмотрим, что произойдёт, если не использовать предзагрузку. Браузер может начать загрузку ресурсов только после того, как обнаружил, что он в них нуждается. Это быстрее всего происходит для ресурсов, которые находятся в HTML во время первоначального разбора документа (например,
Медленнее скачиваются ресурсы, которые обнаруживаются после построения дерева рендеринга (это место, где страница тормозит из-за загрузки шрифта, ведь чтобы понять это, сначала нужно загрузить таблицу стилей, разобрать её, построить объектную модель CSS, а затем дерево рендеринга).
Ещё медленнее загружаются ресурсы, которые добавлены в документ с помощью загрузчиков JavaScript, которые запускаются событиями вроде
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/e8a/cb5/705/e8acb5705ec028771d0426c9e16b8635.png)
В последние несколько лет ситуация улучшилась благодаря Link rel=preload. Например:
Благодаря этим директивам браузер может начать загрузку ресурсов сразу после получения заголовков и перед началом анализа тела HTML:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/54c/fe2/727/54cfe2727bf9fdbb286acf29c173885b.png)
Это существенное улучшение, особенно для больших страниц и критических ресурсов, которые в противном случае будут обнаружены поздно. Особенно для шрифтов, но что угодно может стать критическим ресурсом, например, файл данных, необходимый для загрузки приложения JavaScript.
Однако мы можем добиться большего. Ведь браузер ничего не делает между моментом, когда заканчивает отправку запроса, и когда получает первые байты ответа (большой зелёный фрагмент на начальном запросе выше).
С другой стороны, сервер действительно занят. Он генерирует ответ и определяет, успешен тот или нет. После доступа к базе данных, вызовов API, проверки подлинности и т.д. сервер может решить, что правильным ответом является ошибка 404.
Иногда время размышлений сервера меньше, чем задержка сети. Иногда существенно больше. Но важно понимать, что они не пересекаются. Пока сервер думает, мы не отправляем данные клиенту.
Но интересно, что ещё до генерации ответа вы уже знаете некоторые стили и шрифты, которые необходимо загрузить для отображения страницы. В конце концов, страницы ошибок обычно используют тот же фирменный стиль и дизайн, что и обычные страницы. Было бы здорово отправить эти заголовки
Сервер может записать первый, так называемый «информационный» код ответа, как только получит запрос, и выдать его в сеть. Затем займётся определением реальной реакции и её генерацией. Между тем в браузере можно намного раньше начать предварительную загрузку:
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/d9b/8ca/1cb/d9b8ca1cbb8fd4753bd037893ea92d1b.png)
Конечно, это потребует определённых изменений в работе браузеров, серверов и CDN, и разработчики некоторых браузеров высказали оговорки относительно трудностей реализации. Поэтому до сих пор неясно, когда данные заголовки можно внедрить в эксплуатацию. Вы можете отслеживать прогресс в публичных трекерах для Chrome и Firefox.
Мы рассчитываем, что в конечном итоге вы сможете выдавать заголовки Early Hints прямо из Fastly, по-прежнему отправляя запросы стандартным образом. Мы ещё не решили, как выставить интерфейс через VCL, поэтому сообщите, если у вас есть пожелания на этот счёт!
С HTTP/2 идёт новая технология под названием Server Push, которая вроде бы решает ту же проблему, что и Link rel=preload в ответе Early Hints. Хотя она действительно работает (и вы даже можете быстро генерировать кастомные пуши с edge-серверов в Fastly), но есть существенная разница по нескольким пунктам:
Так или иначе, Early Hints и Server Push предлагают разные компромиссы. Подсказки Early Hints обеспечивают более эффективное использование сети в обмен на дополнительный обмен пакетами. Если вы предполагаете малую задержку в сети и длительное время на обдумывание сервера, то Early Hints — правильное решение.
Однако, это не всегда так. Давайте будем оптимистами и представим, что люди скоро поселятся на Марсе. Они будут просматривать веб-страницы с задержкой 20-45 минут на каждый обмен пакетами, поэтому дополнительный обмен исключительно болезненен, а серверное время несущественно по сравнению с ним. Здесь легко побеждает Server Push. Но если мы когда-либо будем просматривать веб-страницы с Марса, то скорее скачаем какой-то пакет с данными, нечто вроде предлагаемых сейчас веб-пакетов и подписанных обменов.
Хотя Early Hints предполагается использовать в первую очередь в браузере, но есть интересная потенциальная выгода и для CDN. Когда Fastly получает много запросов на один и тот же ресурс, мы обычно отправляем источнику только один из них, а остальные помещаем в очередь ожидания. Этот процесс известен как свёртывание запроса (request collapsing). Если ответ от источника включает в себя
Мы не можем принять решение, пока не получен ответ на первый запрос, но в случае поддержки Early Hints, если сервер выдал ответ Early Hints с заголовком Cache-Control, то мы намного раньше узнаем, что очередь нельзя свернуть в один запрос, а вместо этого немедленно направим источнику все запросы из очереди.
Ранние подсказки — отличный способ получить доступ к некоторым из самых ценных объектов в очереди (водопаде): когда сеть простаивает, то пользователь ждёт, в пути только один запрос, и на экране ничего нет. Но как только загружен HTML и страница проанализирована, то резко возрастает количество ресурсов, которые нуждаются в загрузке. Теперь важно не загружать ресурсы как можно быстрее, а загружать их в правильном порядке. Браузеры используют поразительно сложный массив эвристик, чтобы самостоятельно определить приоритет загрузки, но если вы хотите переопределить их, то в будущем это можно будет сделать с помощью подсказок приоритета (Priority Hints):
С помощью этого нового атрибута важности разработчики могут управлять порядком загрузки ресурсов в случае конкуренции за сеть. Возможно, ресурсы низкого приоритета можно отложить до тех пор, пока процессор и сеть не освободятся, или в зависимости от типа устройства.
Ни ранние подсказки, ни подсказки приоритета пока не стали стандартом. Недавно H2O, сервер HTTP/2, используемый и поддерживаемый Fastly, начал применять Early Hints (см. PR 1727 и 1767), и есть намерение реализовать Priority Hints в Chrome, также как активное отслеживание ответов 1xx. В то же время нет никакого вреда в том, чтобы уже сейчас начать отправлять Early Hints — и если вы хотите опередить тренд, то вперёд!
Вы когда-нибудь загружали сайт на телефоне — и смотрели в течение 10 секунд на страницу без текста? Никто не любит сидеть и глядеть на пустой экран, пока загружается какой-то необычный шрифт. Поэтому есть смысл перенести загрузку таких важных вещей на максимально раннее время. Предзагрузка Link rel=preload должна была частично решить проблему. Первым делом браузер парсит HTTP-заголовки, так что это идеальное место, чтобы указать на предварительную загрузку ресурса, который точно понадобится позже.
По умолчанию интернет работает медленно
Давайте посмотрим, что произойдёт, если не использовать предзагрузку. Браузер может начать загрузку ресурсов только после того, как обнаружил, что он в них нуждается. Это быстрее всего происходит для ресурсов, которые находятся в HTML во время первоначального разбора документа (например,
<script>
, <link rel=stylesheet>
и <img>
).Медленнее скачиваются ресурсы, которые обнаруживаются после построения дерева рендеринга (это место, где страница тормозит из-за загрузки шрифта, ведь чтобы понять это, сначала нужно загрузить таблицу стилей, разобрать её, построить объектную модель CSS, а затем дерево рендеринга).
Ещё медленнее загружаются ресурсы, которые добавлены в документ с помощью загрузчиков JavaScript, которые запускаются событиями вроде
DOMContentLoaded
. Если собрать всё вместе, мы получаем неоптимизированный и довольно бессмысленный водопад. Значительную часть времени канал простаивает, а ресурсы загружаются или раньше, чем необходимо, или слишком поздно:![](https://habrastorage.org/getpro/habr/post_images/e8a/cb5/705/e8acb5705ec028771d0426c9e16b8635.png)
Link rel=preload очень помогает
В последние несколько лет ситуация улучшилась благодаря Link rel=preload. Например:
Link: </resources/fonts/myfont.woff>; rel=preload; as=font; crossorigin
Link: </resources/css/something.css>; rel=preload; as=style
Link: </resources/js/main.js>; rel=preload; as=script
Благодаря этим директивам браузер может начать загрузку ресурсов сразу после получения заголовков и перед началом анализа тела HTML:
![](https://habrastorage.org/getpro/habr/post_images/54c/fe2/727/54cfe2727bf9fdbb286acf29c173885b.png)
Это существенное улучшение, особенно для больших страниц и критических ресурсов, которые в противном случае будут обнаружены поздно. Особенно для шрифтов, но что угодно может стать критическим ресурсом, например, файл данных, необходимый для загрузки приложения JavaScript.
Однако мы можем добиться большего. Ведь браузер ничего не делает между моментом, когда заканчивает отправку запроса, и когда получает первые байты ответа (большой зелёный фрагмент на начальном запросе выше).
Задействуем время «размышления сервера» с помощью «ранних подсказок»
С другой стороны, сервер действительно занят. Он генерирует ответ и определяет, успешен тот или нет. После доступа к базе данных, вызовов API, проверки подлинности и т.д. сервер может решить, что правильным ответом является ошибка 404.
Иногда время размышлений сервера меньше, чем задержка сети. Иногда существенно больше. Но важно понимать, что они не пересекаются. Пока сервер думает, мы не отправляем данные клиенту.
Но интересно, что ещё до генерации ответа вы уже знаете некоторые стили и шрифты, которые необходимо загрузить для отображения страницы. В конце концов, страницы ошибок обычно используют тот же фирменный стиль и дизайн, что и обычные страницы. Было бы здорово отправить эти заголовки
Link: rel=preload
ещё перед работой сервера. Именно для этого задуман стандарт «ранних подсказок» Early Hints, указанный в RFC8297 от Рабочей группы HTTP за авторством моего коллеги Кадзухо Оку из Fastly. Оцените магию нескольких строк состояния в одном ответе:HTTP/1.1 103 Early Hints
Link: <some-font-face.woff2>; rel="preload"; as="font"; crossorigin
Link: <main-styles.css>; rel="preload"; as="style"
HTTP/1.1 404 Not Found
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: <some-font-face.woff2>; rel="preload"; as="font"; crossorigin
Link: <main-styles.css>; rel="preload"; as="style"
Сервер может записать первый, так называемый «информационный» код ответа, как только получит запрос, и выдать его в сеть. Затем займётся определением реальной реакции и её генерацией. Между тем в браузере можно намного раньше начать предварительную загрузку:
![](https://habrastorage.org/getpro/habr/post_images/d9b/8ca/1cb/d9b8ca1cbb8fd4753bd037893ea92d1b.png)
Конечно, это потребует определённых изменений в работе браузеров, серверов и CDN, и разработчики некоторых браузеров высказали оговорки относительно трудностей реализации. Поэтому до сих пор неясно, когда данные заголовки можно внедрить в эксплуатацию. Вы можете отслеживать прогресс в публичных трекерах для Chrome и Firefox.
Мы рассчитываем, что в конечном итоге вы сможете выдавать заголовки Early Hints прямо из Fastly, по-прежнему отправляя запросы стандартным образом. Мы ещё не решили, как выставить интерфейс через VCL, поэтому сообщите, если у вас есть пожелания на этот счёт!
Но что насчёт HTTP/2 Server Push?
С HTTP/2 идёт новая технология под названием Server Push, которая вроде бы решает ту же проблему, что и Link rel=preload в ответе Early Hints. Хотя она действительно работает (и вы даже можете быстро генерировать кастомные пуши с edge-серверов в Fastly), но есть существенная разница по нескольким пунктам:
- Сервер не знает о наличии ресурса у клиента, поэтому часто пушит его без необходимости. Из-за буферизации сети и задержки клиент обычно не может отменить отправку перед получением всего содержимого. (Хотя есть возможное решение этой проблемы, в виде предлагаемого заголовка Cache Digest, над которым Кадзухо работает вместе с Йоавом Вайсом из Akamai).
- Запушенные ресурсы привязаны к соединению, поэтому легко запушить ресурс, который клиент не использует, так как он пытается получить его через другое соединение. Клиентам может потребоваться использовать другое подключение, поскольку ресурс находится в другом источнике с другим сертификатом TLS или поскольку он извлекается в другом режиме учётных данных.
- H2 Push не очень последовательно реализован в разных браузерах. Поэтому трудно предсказать, будет он работать или нет в вашем конкретном случае.
Так или иначе, Early Hints и Server Push предлагают разные компромиссы. Подсказки Early Hints обеспечивают более эффективное использование сети в обмен на дополнительный обмен пакетами. Если вы предполагаете малую задержку в сети и длительное время на обдумывание сервера, то Early Hints — правильное решение.
Однако, это не всегда так. Давайте будем оптимистами и представим, что люди скоро поселятся на Марсе. Они будут просматривать веб-страницы с задержкой 20-45 минут на каждый обмен пакетами, поэтому дополнительный обмен исключительно болезненен, а серверное время несущественно по сравнению с ним. Здесь легко побеждает Server Push. Но если мы когда-либо будем просматривать веб-страницы с Марса, то скорее скачаем какой-то пакет с данными, нечто вроде предлагаемых сейчас веб-пакетов и подписанных обменов.
Дополнительный бонус: ускоренное свёртывание запроса
Хотя Early Hints предполагается использовать в первую очередь в браузере, но есть интересная потенциальная выгода и для CDN. Когда Fastly получает много запросов на один и тот же ресурс, мы обычно отправляем источнику только один из них, а остальные помещаем в очередь ожидания. Этот процесс известен как свёртывание запроса (request collapsing). Если ответ от источника включает в себя
Cache-Control: private
, то следует убрать из очереди все запросы и отправить источнику их по отдельности, потому что мы не можем использовать один ответ для удовлетворения нескольких запросов.Мы не можем принять решение, пока не получен ответ на первый запрос, но в случае поддержки Early Hints, если сервер выдал ответ Early Hints с заголовком Cache-Control, то мы намного раньше узнаем, что очередь нельзя свернуть в один запрос, а вместо этого немедленно направим источнику все запросы из очереди.
Заказ менее критического контента с подсказками о приоритете
Ранние подсказки — отличный способ получить доступ к некоторым из самых ценных объектов в очереди (водопаде): когда сеть простаивает, то пользователь ждёт, в пути только один запрос, и на экране ничего нет. Но как только загружен HTML и страница проанализирована, то резко возрастает количество ресурсов, которые нуждаются в загрузке. Теперь важно не загружать ресурсы как можно быстрее, а загружать их в правильном порядке. Браузеры используют поразительно сложный массив эвристик, чтобы самостоятельно определить приоритет загрузки, но если вы хотите переопределить их, то в будущем это можно будет сделать с помощью подсказок приоритета (Priority Hints):
<script src="main.js" async importance="high"></script>
<script src="non-critical-1.js" async importance="low"></script>
С помощью этого нового атрибута важности разработчики могут управлять порядком загрузки ресурсов в случае конкуренции за сеть. Возможно, ресурсы низкого приоритета можно отложить до тех пор, пока процессор и сеть не освободятся, или в зависимости от типа устройства.
Это можно использовать?
Ни ранние подсказки, ни подсказки приоритета пока не стали стандартом. Недавно H2O, сервер HTTP/2, используемый и поддерживаемый Fastly, начал применять Early Hints (см. PR 1727 и 1767), и есть намерение реализовать Priority Hints в Chrome, также как активное отслеживание ответов 1xx. В то же время нет никакого вреда в том, чтобы уже сейчас начать отправлять Early Hints — и если вы хотите опередить тренд, то вперёд!