- BrainTools - https://www.braintools.ru -

Как мы отслеживаем производительность веб-сервисов, или Дело «Скорости»

Как мы отслеживаем производительность веб-сервисов, или Дело «Скорости» - 1

Салют, Хабр!

Я Паша, вхожу в группу обеспечения производительности интерфейсов. Эту статью мы написали с Сергеем @TrueNort [1] — руководителем группы. В SberDevices её называют командой «Скорость». Под надзором «Скорости» более двадцати веб-сервисов, каждый из которых должен работать быстро и точно. А значит, нужна система мониторинга производительности с гибкими настройками, чуткой реакцией [2] на изменения и оперативными сообщениями о проблемах.  

В статье расскажем, зачем мы нормируем метрики логарифмами, как скрипт превращает данные из ClickHouse в алёрты и как удобнее отображать данные. Словом, поделимся нашим опытом [3] контроля производительности веб-ресурсов.

Когда канвасы были маленькими  

Давным-давно (в 2020-х) в нашей компании произошёл кембрийский взрыв веб-технологий. Он объяснялся появлением ассистента Салют [4] на разных поверхностях (например, SberBox и SberPortal) и экосистемы для разработки смартаппов сторонними разработчиками. Канвасы позволяли легко и удобно разрабатывать и выводить новые проекты, а значит, экспериментировать с подходами и оценивать отклик пользователей. 

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

  1. Свой opensource-пакет для Web Telemetry (код на GitHub [5]).

  2. ClickHouse — собственный closed-source сервер для вставки данных из Web Telemetry. Настроенные materialized view позволяют преобразовывать значения из сырых данных в агрегированную таблицу там же в БД.

  3. Grafana с дашбордами для всех сервисов.

  4. Каналы на корпоративной рабочей платформе, куда передаются алёрты из ClickHouse при изменении метрик. Об алёртах расскажем ниже.

Мы мониторим более двадцати сервисов и сайтов: ГигаЧат [6], sberdevices.ru [7]… В сумме безотказность и производительность множества веб-ресурсов — в руках команды «Скорости». 

Система мониторинга отслеживает метрики, которые показывают, как отрабатывает сервис. Это CLS, INP, LCP — они же Core Web Vitals от Google, отраслевой стандарт фронтенда; снова отошлёмся к предыдущей статье [8]. И FCP (first contentful paint) — промежуток от начала загрузки страницы до момента отображения какой-либо части контента страницы на экране.  

Мониторятся также метрики navigation timing для сервисов и сайтов (подробнее о них здесь [9]). Например, TTFB (time to first byte) — как понятно из названия, промежуток от начала загрузки страницы до получения первого байта информации от сервера. В него входят перенаправление, DNS-поиск, соединение и согласование TLS, сам запрос. Чем меньше значение TTFB, тем выше скорость загрузки контента. Мы отслеживаем, а значит, отображаем для команд фронтенда, TTFB по регионам и постранично. Первое на случай запросов из региона с нестабильным интернетом, второе — чтобы оперативно отслеживать «плохие» страницы.

Кроме того, мы отслеживаем кастомные метрики, данные для которых собираем сами (то есть без существующего API). Например:

FMP (first meaningful paint) — время до получения от бэкенда данных для отрисовки первого значимого контента. Изначально мы использовали одноименную метрику Google, но, во-первых, начиная с версии LightHouse 6.0 её заменил LCP. Во-вторых, команды сами точнее понимают, какой компонент интерфейса в сервисе значим. Поэтому начали использовать под тем же именем собственную метрику. 

reactRenderStart и reactRenderEnd. Как можно догадаться, первая определяет начало первого рендеринга React, а вторая — его окончание.  

FrameTime — интервал между последовательными вызовами requestAnimationFrame. Позволяет оценить, успевает ли браузер подготовить и отрисовать новые кадры с целевой частотой (например, 60 кадров/с), и выявить перегрузку очереди отрисовки. 

Long Task — трудоёмкая задача (например, перерисовка или обработка события), выполнение которой занимает более 50 мс. PerformanceObserverAPI сам считает всё и выбирает, какие задачи пометить как Long Task.

Что такое хорошо и что такое плохо?

Это ключевой вопрос, который задают фронтенд-разработчики сервисов команде «Скорости». Как показала практика, нам удобен большой набор разнообразных метрик, но фронтендеры просто хотят понимать: насколько сайт или сервис быстрый? И если начал тормозить — что не так? 

Выходило, что нужна сводная метрика, которая будет наглядно отображать производительность сервиса. За пример мы взяли Lighthouse от Google Chrome. В ней используется [10] оценочная кривая на основе логнормального распределения. Она связана с особенностями работы нашего восприятия [11] согласно закону Вебера-Фехнера [12]: физическая шкала сжимается в шкалу восприятия логарифмически. Проще говоря, люди замечают изменение, только если его интенсивность отличается от предыдущего варианта в геометрической прогрессии.

Похожим образом мы разработали собственную метрику производительности сайта — Speed Score. В неё включены четыре параметра:

LCP — дольше всех существующая из метрик Core Web Vitals, она наглядно отображает оптимизацию как самих изображений, так и получения/доставки данных. По сути, хороший LCP обозначает, что связанные с ним показатели также в порядке.   

FID — first input delay — первое нажатие на кнопку. Внимание [13], этот показатель отличается от INP в Web Vital Core — там считаются все взаимодействия с сайтом. Собственно, INP заменил FID в Web Vital Core.

ImgSize Score —  размер картинки на 75-м процентиле.

JsTotal Score — сумма всех скриптов, загруженных на странице.

Сервис или сайт оценивается по каждому из параметров по шкале от 0 до 100 в зависимости от положения измеренного значения на кривой логнормального распределения:

text{Балл}=100 times (1 - text{CDF}(text{значение}))

где CDF — интегральная функция распределения.

Медиана (50-й процентиль) даёт 50 баллов, а 10-й процентиль — 90 баллов (поскольку только 10% быстрее). Эти пороги заданы на основе нашей собственной статистики, в отличии от LightHouse; там они берутся из общемирового веб-архива, что даёт «среднюю температуру по больнице».

SpeedScore комбинирует оценки показателей с присвоенным каждому из показателей весовым коэффициентом. Формула звучит как:

text{SpeedScore}=text{LCP} times 0{,}4 + text{FID} times 0{,}2 + text{ImgSize,Score} times 0{,}2 + text{JSTotal} times 0{,}2

Кроме того, мы оцениваем Speed Score для канвасов, но по немного другой формуле — с участием упомянутых в первой части метрик. Вдобавок формула зависит от того, измеряется ли для канваса FrameTime или нет. С этой метрикой:

text{Speed,Score}=text{FMP} times 0{,}4 + text{FCP} times 0{,}3 + text{Input,Delay} times 0{,}15 + text{FrameTime} times 0{,}15

И без FrameTime:

text{Speed,Score}=text{FMP} times 0{,}5 + text{FCP} times 0{,}3 + text{ImageScore} times 0{,}2

В Grafana на соответствующем дашборде Speed Score сервиса отображается в делении на три зоны: красная (плохо), жёлтая (можно и лучше), зелёная (хорошо). Таким образом, коллеги сразу же видят, в какой зоне они находятся и нужно ли что-то улучшать. Это позволяет легко делиться своими результатами — достаточно сказать: «У нас сервис в зелёной зоне», а также ставить цели для команд: вывести сервис в зелёную зону, увеличить значение Speed Score на N пунктов. Словом, благодаря наглядности метрика стала повседневной реальностью фронтендеров. А при желании они могут просмотреть в Grafana отдельные значения метрик. 

Как мы отслеживаем производительность веб-сервисов, или Дело «Скорости» - 6

Изначально Speed Score считали за небольшой промежуток времени, но…  оптимизация сайта — это месяц работы. А значит, нужна динамика изменения показателя на большом отрезке. Проблема в том, что мы считали параметры для оценки Speed Score, например, JS Score для оценки Total, для каждой сессии в real time — то есть обрабатывали десятки тысяч сессий, в каждой из которой сотни ресурсов, непосредственно во время запроса. Эти запросы были слишком тяжеловесны: для примера, около 10 млн значений данных только для одного сервиса — 170 тыс. сессий в день, 50-70 ресурсов (параметров и значений) за сессию. Наш сервер ClickHouse плохо справлялся с таким объёмом, поэтому и контрольный промежуток был небольшой. 

Для оптимизации запросов и подготовки данных применили materialized view. Промежуточные значения параметров агрегируются в соответствующую таблицу, как только в таблице ресурсов любого из сервисов появляется запись. Так удалось уменьшить нагрузку более чем в сотню раз и заодно расширить временной диапазон оценки Speed Score. 

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

Скрипт для materialized view
CREATE MATERIALIZED VIEW IF NOT EXISTS default.xxxx_xxxx_resources_aggregate_mv on cluster 'metrics' TO default.xxxx_xxxx_resources_aggregate AS
SELECT sessionId,
        hostname,
        min(timestamp) as timestamp,
        sumIf(encodedBodySize, isScript(name, initiatorType)) as jsTotal,
        sumIf(encodedBodySize, isInMainJSBundle(name, initiatorType)) as jsMainBundle,
        avgStateIf( encodedBodySize,
            isImg(name, initiatorType)
        ) as imgSizeAvg,
        avgStateIf( duration,
            isImg(name, initiatorType)
        ) as imgDurationAvg,
        avgStateIf( transferSize,
            isImg(name, initiatorType)
        ) as imgTransferSizeAvg,
        count() as count
FROM default.xxxx_xxxx_resources
GROUP BY sessionId, hostname
Как мы отслеживаем производительность веб-сервисов, или Дело «Скорости» - 7 [14]

Алёрт! Алёрт!

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

Алёрты в Grafana для наших кейсов не подошли. Поэтому сделали своё решение, которое напрямую «ходит» в ClickHouse. Ежедневно в 9:00 прогоняется скрипт для всех метрик каждого сервиса. Алгоритм алёртов основан на анализе динамики квантилей. Он работает так:

  1. Формируются референсные значения. Временной ряд разбивается на последовательные временные окна — например, календарные дни. Для каждого такого окна рассчитывается заданный квантиль. 

  2. Определяется базовый порог. Из полученного набора дневных квантилей строится новая выборка. За пороговое значение принимается квантиль этой выборки (например, 75-й процентиль от распределения дневных квантилей, а у CLS — 95-й, так как у этой метрики подавляющее большинство значений — нулевые. Если брать 75-й перцентиль, порог всегда будет нулевым, и мы пропустим редкие, но критичные деградации. 95-й перцентиль позволяет ловить именно такие случаи). По сути, мы находим «типичный максимум» для дневных показателей.

  3. Рассчитывается текущее значение. Тот же квантиль для последних суток (текущего дня).

  4. Сравнивается текущее значение и установленный исторический порог. Если текущее значение выше порога на заранее заданную константу (фиксированную величину), срабатывает алёрт.

Если сравнение показало рост метрики, то в канале корпоративной рабочей платформы команды выстреливает алёрт со значением метрики, уровнем лимита и ссылкой на дашборд. Там же — ссылка на Wiki команды «Скорость» с предложениями, что пошло не так и как можно проверить это.

Кроме того, в систему алёртов встроен агент с искусственным интеллектом [15] ГигаЧат — его удалось легко подключить как раз благодаря тому, что мы использовали собственное решение, а не алёрты в Grafana. ГигаЧат способен проанализировать данные по метрике, на которую сработал алёрт, объяснить владельцу сервиса, в чём может быть проблема и как это исправить. При этом он анализирует не просто данные алёрта, а данные за последние десять дней. Это позволяет увидеть весь процесс в динамике: извлечь из тысяч строк логов причину и суть событий. 

Как мы отслеживаем производительность веб-сервисов, или Дело «Скорости» - 8

Система мониторинга производительности «Скорости» даёт контроль над скоростью сервисов, алёрты же обеспечивают своевременное оповещение о проблемах. Например:

  • Прилетает алёрт: на одном из сайтов в два раза деградировала метрика FCP. Страница сайта состоит из футера, хедера и карточки товара. Проверка показала, что карточка перестала пре-рендериться на сервере и теперь собирается на клиенте, а значит, замедляет страницу. Разработчики не могли заметить это — у них элемент рендерился почти моментально — а у пользователей загрузка деградировала. Исправили проблему и дополнили сервисы тестами на пре-рендер, чтобы не допустить её повторения [16].

  • Получили алёрт на одном из лэндингов: резко выросла метрика INP — создавалось впечатление [17], что сайт завис. Причин оказалось сразу две: во-первых, избыточная отправка Яндекс.Метрики, которая загружала основной поток приложения и мешала загрузиться карусели с анимацией. Во-вторых, подключенный от метрики сервис, которым никто не пользуется.  Соответственно, применили функцию requestIdleCallback, которая откладывала вызов Яндекс.Метрики до освобождения основного потока. А лишний сервис отключили.

  • Алёрт сообщил о деградации FCP и FMP на главной странице сайта аккурат после свежего релиза. Оказалось, что команда зарелизила новую карусель на главной, но по некой причине при каждой прокрутке картинки загружались заново (в тестировании проблема не была заметна). Откатили релиз, исправили карусель.

  • На одном из сервисов ухудшился Speed Score в целом. «Провалившись» в метрики, увидели скачок ImgSize на одном из ресурсов. Расследование показало, что разработчики ресурса не уследили за оптимизацией изображения, поэтому одно изображение вместо 50 Кб начало весить 3 Мб. Так как оно было на популярной странице, а значит, его часто скачивали, выросла и метрика ImgSize.

Заключение

По сути цель команды «Скорости» — помогать другим командам. Мы обеспечиваем работу сервисов, которые посещают сотни тысяч пользователей. У нас действует фабрика фронтов — если разворачивается какой-то быстрый сервис или сайт, это тоже к нам. Система мониторинга производительности позволяет быстрее замечать проблемы, удерживая все наши сервисы в зелёной зоне Speed Score. Словом, «Скорость» всегда в центре событий.

Автор: AvoiDd

Источник [18]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/26563

URLs in this post:

[1] @TrueNort: https://www.braintools.ru/users/truenort

[2] реакцией: http://www.braintools.ru/article/1549

[3] опытом: http://www.braintools.ru/article/6952

[4] ассистента Салют: https://salute.sber.ru/

[5] на GitHub: https://github.com/salute-developers/web-telemetry

[6] ГигаЧат: https://giga.chat/

[7] sberdevices.ru: http://sberdevices.ru

[8] предыдущей статье: https://habr.com/ru/companies/sberdevices/articles/940578/

[9] здесь: https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Performance/Measuring_performance#performance_apis

[10] используется: https://developer.chrome.com/docs/lighthouse/performance/performance-scoring?hl=ru#metric-scores

[11] восприятия: http://www.braintools.ru/article/7534

[12] закону Вебера-Фехнера: https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BA%D0%BE%D0%BD_%D0%92%D0%B5%D0%B1%D0%B5%D1%80%D0%B0_%E2%80%94_%D0%A4%D0%B5%D1%85%D0%BD%D0%B5%D1%80%D0%B0

[13] Внимание: http://www.braintools.ru/article/7595

[14] Image: https://sourcecraft.dev/

[15] интеллектом: http://www.braintools.ru/article/7605

[16] повторения: http://www.braintools.ru/article/4012

[17] впечатление: http://www.braintools.ru/article/2012

[18] Источник: https://habr.com/ru/companies/sberdevices/articles/1006020/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1006020

www.BrainTools.ru

Rambler's Top100