Как оценить производительность загрузки в полевых условиях с помощью Navigation Timing и Resource Timing

Изучите основы использования API навигации и синхронизации ресурсов для оценки производительности загрузки в реальных условиях.

Опубликовано: 8 октября 2021 г.

Если вы использовали ограничение скорости соединения в панели управления сетью в инструментах разработчика браузера (или Lighthouse в Chrome) для оценки производительности загрузки, вы знаете, насколько удобны эти инструменты для оптимизации производительности. Вы можете быстро измерить влияние оптимизации производительности при стабильной и постоянной базовой скорости соединения. Единственная проблема заключается в том, что это синтетическое тестирование, которое дает данные лабораторных условий , а не полевых .

Синтетическое тестирование само по себе не является чем-то плохим , но оно не отражает реальную скорость загрузки вашего сайта для реальных пользователей. Для этого необходимы данные из полей, которые можно получить с помощью API Navigation Timing и Resource Timing.

API-интерфейсы, которые помогут вам оценить производительность погрузки в полевых условиях.

API Navigation Timing и Resource Timing — это два похожих API, имеющих значительное совпадение, которые измеряют две разные вещи:

  • Показатель Navigation Timing измеряет скорость запросов к HTML-документам (то есть, запросов навигации).
  • Resource Timing измеряет скорость запросов к ресурсам, зависящим от документа, таким как CSS, JavaScript, изображения и другие типы ресурсов.

Эти API предоставляют свои данные в буфере записей производительности , к которому можно получить доступ в браузере с помощью JavaScript. Существует несколько способов запроса к буферу производительности, но наиболее распространенный — использование performance.getEntriesByType :

// Get Navigation Timing entries:
performance.getEntriesByType('navigation');

// Get Resource Timing entries:
performance.getEntriesByType('resource');

performance.getEntriesByType принимает строку, описывающую тип записей, которые вы хотите получить из буфера записей производительности. Методы 'navigation' и 'resource' получают данные о времени выполнения для API Navigation Timing и Resource Timing соответственно.

Объем информации, предоставляемой этими API, может быть огромным, но они являются вашим ключом к измерению производительности загрузки в реальных условиях, поскольку вы можете собирать эти данные от пользователей во время посещения вашего веб-сайта.

Время жизни и временные параметры сетевого запроса

Сбор и анализ данных о времени навигации и использования ресурсов чем-то сродни археологии: вы воссоздаёте мимолетную жизнь сетевого запроса постфактум. Иногда полезно визуализировать концепции, и в случае с сетевыми запросами могут помочь инструменты разработчика вашего браузера.

Сетевые параметры, отображаемые в инструментах разработчика Chrome. Показаны параметры обработки запросов: постановка запроса в очередь, согласование соединения, сам запрос и ответ (цветные полосы).
Визуализация сетевого запроса на панели «Сеть» в инструментах разработчика Chrome.

Жизненный цикл сетевого запроса состоит из различных фаз, таких как поиск DNS, установление соединения, согласование TLS и другие источники задержки. Эти временные параметры представлены в виде DOMHighResTimestamp . В зависимости от вашего браузера, точность измерения времени может составлять микросекунды или округляться до миллисекунд. Вам следует подробно изучить эти фазы и их связь с временем навигации и временем использования ресурсов.

поиск DNS

Когда пользователь переходит по URL-адресу, система доменных имен (DNS) запрашивается для преобразования домена в IP-адрес. Этот процесс может занять значительное время — время, которое вы захотите измерить в полевых условиях. Функции Navigation Timing и Resource Timing предоставляют два параметра, связанных с DNS:

  • domainLookupStart обозначает момент начала поиска DNS-запроса.
  • domainLookupEnd — это момент завершения поиска DNS.

Общее время поиска DNS можно рассчитать, вычитая начальное значение из конечного значения:

// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;

согласование соединения

Ещё одним фактором, влияющим на производительность загрузки, является согласование соединения, то есть задержка, возникающая при подключении к веб-серверу. Если используется HTTPS, этот процесс также включает время согласования TLS. Фаза подключения состоит из трёх этапов:

  • connectStart — это событие, при котором браузер начинает устанавливать соединение с веб-сервером.
  • Параметр secureConnectionStart указывает момент начала согласования TLS-соединения клиентом.
  • connectEnd означает, что соединение с веб-сервером установлено.

Измерение общего времени соединения аналогично измерению общего времени поиска DNS: вы вычитаете время начала из времени окончания. Однако существует дополнительное свойство secureConnectionStart , которое может быть равно 0 , если HTTPS не используется или если соединение является постоянным . Если вы хотите измерить время согласования TLS, вам нужно это учитывать:

// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with

// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
  // Awesome! Calculate it!
  tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}

После завершения поиска DNS и согласования соединения вступают в действие временные параметры, связанные с получением документов и зависимых от них ресурсов.

Запросы и ответы

На эффективность нагружения влияют два типа факторов:

  • Внешние факторы: это такие вещи, как задержка и пропускная способность. Помимо выбора хостинг-провайдера и, возможно, CDN , они (в основном) неподвластны нашему контролю, поскольку пользователи могут получить доступ к сети из любой точки мира.
  • Внутренние факторы: это такие вещи, как архитектура сервера и клиента, а также размер ресурсов и наша способность оптимизировать их, которые находятся в пределах нашего контроля.

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

  • Параметр fetchStart отмечает момент, когда браузер начинает загрузку ресурса (Resource Timing) или документа для запроса навигации (Navigation Timing). Это предшествует фактическому запросу и является точкой, в которой браузер проверяет кэш (например, экземпляры HTTP и Cache ).
  • Параметр workerStart указывает момент начала обработки запроса в обработчике события fetch сервис-воркера. Значение будет равно 0 если ни один сервис-воркер не управляет текущей страницей.
  • requestStart — это момент, когда браузер отправляет запрос.
  • responseStart — это момент получения первого байта ответа.
  • responseEnd — это момент получения последнего байта ответа.

Эти измерения позволяют оценить различные аспекты производительности загрузки, такие как поиск в кэше внутри сервис-воркера и время загрузки:

// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;

// Service worker time plus response time
let workerTime = 0;

if (pageNav.workerStart > 0) {
  workerTime = pageNav.responseEnd - pageNav.workerStart;
}

Вы также можете измерить другие аспекты задержки запроса и ответа:

const [pageNav] = performance.getEntriesByType('navigation');

// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;

// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;

// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;

Другие измерения, которые вы можете произвести

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

  • Перенаправления страниц: Перенаправления — это часто упускаемый из виду источник дополнительной задержки, особенно цепочки перенаправлений. Задержка возникает различными способами, например, при переходах HTTP-to-HTTPS, а также при перенаправлениях 302/некэшированных перенаправлениях 301. Показатели времени redirectStart , redirectEnd и redirectCount помогают оценить задержку, возникающую при перенаправлении.
  • Выгрузка документа: На страницах, где код выполняется в обработчике события unload , браузер должен выполнить этот код, прежде чем сможет перейти на следующую страницу. unloadEventStart и unloadEventEnd измеряют выгрузку документа.
  • Обработка документов: Время обработки документов может быть несущественным, если ваш веб-сайт не отправляет очень большие HTML-данные. Если это относится к вашей ситуации, вам могут быть полезны данные о времени выполнения событий domInteractive , domContentLoadedEventStart , domContentLoadedEventEnd и domComplete .

Как получить данные о времени выполнения кода

Все приведенные до сих пор примеры используют performance.getEntriesByType , но существуют и другие способы запроса буфера записей производительности, такие как performance.getEntriesByName и performance.getEntries . Эти методы подходят, когда требуется лишь несложный анализ. Однако в других ситуациях они могут привести к чрезмерной нагрузке на основной поток из-за перебора большого количества записей или даже многократного опроса буфера производительности для поиска новых записей.

Рекомендуемый подход для сбора записей из буфера производительности — использование PerformanceObserver . PerformanceObserver отслеживает записи о производительности и предоставляет их по мере добавления в буфер:

// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
  // Get all resource entries collected so far:
  const entries = observedEntries.getEntries();

  // Iterate over entries:
  for (let i = 0; i < entries.length; i++) {
    // Do the work!
  }
});

// Run the observer for Navigation Timing entries:
perfObserver.observe({
  type: 'navigation',
  buffered: true
});

// Run the observer for Resource Timing entries:
perfObserver.observe({
  type: 'resource',
  buffered: true
});

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

Как позвонить домой

После того, как вы соберете все необходимые данные о времени, вы можете отправить их на конечную точку для дальнейшего анализа. Это можно сделать двумя способами: с помощью navigator.sendBeacon или с помощью fetch с включенной опцией keepalive . Оба метода отправят запрос на указанную конечную точку в неблокирующем режиме, и запрос будет поставлен в очередь таким образом, чтобы при необходимости он сохранялся дольше текущей сессии страницы:

// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
  // Caution: If you have lots of performance entries, don't
  // do this. This is an example for illustrative purposes.
  const data = JSON.stringify(performance.getEntries());

  // Send the data!
  navigator.sendBeacon('/analytics', data);
}

В этом примере JSON-строка будет получена в виде POST -запроса, который вы сможете декодировать, обработать и сохранить в бэкэнде приложения по мере необходимости.

Заключение

После сбора метрик вам предстоит выяснить, как анализировать эти данные. При анализе данных следует придерживаться нескольких общих правил, чтобы делать обоснованные выводы:

  • Избегайте использования средних значений , поскольку они не отражают опыт конкретного пользователя и могут быть искажены выбросами.
  • Полагайтесь на процентили. В наборах данных, содержащих показатели производительности, зависящие от времени, чем ниже значение, тем лучше. Это означает, что, отдавая приоритет низким процентилям, вы обращаете внимание только на самые быстрые результаты.
  • Отдавайте приоритет длинному хвосту ценностей . Когда вы отдаете приоритет опыту, находящемуся на 75-м процентиле или выше, вы сосредотачиваете внимание там, где ему и место: на самом медленном опыте.

Данное руководство не претендует на исчерпывающий характер по вопросам навигации или синхронизации ресурсов, а является лишь отправной точкой. Вот несколько дополнительных ресурсов, которые могут оказаться полезными:

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