Выпуск №22 — Blackfire.io

Обзор и первые впечатления от сервиса профилирования PHP кода Blackfire.io

Привет мир!
Вы слушаете «Пятиминутку PHP», выпуск номер 22 — подкаст о новостях из мира PHP, интересных постах в блогах и современных подходах к разработке.

На неделе я эксперементировал с профилировщиком кода для PHP: Blackfire.io и хочу поделиться первыми впечатлениями.

Начну с матчасти, процитирую википедию:

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

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

Традиционно в мире PHP популярны два профайлера: XHProf и Xdebug.

Первый профайлер, XHProf – это классическое PECL расширение, последняя версия которого вышла в сентябре 2013 года. Скорее всего, он не работает с PHP 7, я не проверял. На хабре есть подробная статья 2012 года о профилировании и отладке PHP-приложений с помощью XHProf и FirePHP от @KSDaemon. Не знаю на сколько это актуально, но я припоминаю, что сам когда-то использовал FirePHP, ностальгия…

Второй профайлер, Xdebug – это в первую очередь всем известный отладчик, который умеет и профилировать. Xdebug активно поддерживается и развивается, отлично работает с PHP 7, подключается в виде расширения для PHP. Важное замечание: его не рекомендуется ставить на production сервере.

Расскажу как я пользуюсь профайлером встроенным в Xdebug.

Для начала нужно установить в панель закладок браузера так называемые букмарклеты. Кстати, их невозможно установить в браузере Edge! Букмарклет представляет собой ссылку, которая содержит простейший JavaScript код, устанавливающий cookie XDEBUG_PROFILE=1. Есть и второй букмарклет, который убирает это значение из cookie.

Итак, я захожу на страницу своего приложения на машине, где подключено расширение Xdebug, нажимаю на букмарклет. В cookie для текущего домена (например, localhost) устанавливается значение XDEBUG_PROFILE=1 и следующий переход по ссылке в рамках этого же домена (т.е. localhost) запустит на сервере процесс профилирования PHP кода.

Профилировщик измерит время выполнения всех функций и методов, а результат будет записан в файл специального формата в папку, которая указана в настройках в php.ini. Этот файл можно открыть несколькими утилитам. В частности, есть графические утилиты, которые покажут граф вызовов, полезно при изучении чужого кода. Но что самое интересное, этот файл можно открыть в PhpStorm через меню Tools -> Analize Xdebug Profiler Snapshot… — вы увидите таблицу с именами всех функций, количеством вызовов и затраченным временем.

Остановлюсь подробнее на двух столбцах: Time и Own Time. Первый столбец Time показывает время выполнения функции и всех вложенных функций. Второй столбец Own Time показывает время выполнения функции за вычетом времени вложенных функций.

Я сразу сортирую таблицу по Own Time, чтобы увидеть самые «долгоиграющие». Тут может быть два варианта: либо функция сама по себе «тяжелая», т.е. занимается вычислениями, либо функция вызывается много раз и в итоге накапливается солидное общее время выполнения. В этом случае стоит посмотреть кто её вызывает и почему это происходит многократно. Возможно, некоторые вызовы по недосмотру попали внутрь цикла или результат работы функции можно мемоизировать (закешировать).

Итого, Xdebug даёт представление о времени выполнения функций, о графе вызовов и имеет хорошую интеграцию с PhpStorm: прямо из результатов профилировщика можно перейти к исходникам по кнопке F4.

Теперь перейдём к Blackfire.io. Основные отличительные особенности две: это SaaS (те облачный сервис) и он платный. Но не спешите закрывать страницу, т.к. есть и бесплатный тарифный план с урезанной функциональностью. Обо всём по порядку.

Blackfire начал мелькать на горизонте где-то пару лет назад. На хабре есть статья за ноябрь 2014 года с обзором бетта версии.

На самом сайте (blackfire.io) он позиционируется как Performance Management Solution. Сервис разработан создателями фреймворка Symfony, т.е. компанией SensioLabs, активно развивается, поддерживает PHP начиная с версии 5.3 и вплоть до 7.1, которая на текущий момент ещё в стадии Release Candidate. Работает под Linux, macOS и Windows. И, что интересно, blackfire можно устанавливать на production сервера без потери производительности!

Схема работы следующая.

Для начала, вы регистрируетесь на сайте balckfire.io. Можно авторизоваться через GitHub, через Google или аккаунтом SensioLabsConnect.

Далее, вы устанавливаете специальное расширение для PHP, которое они называют Probe. Делаете минимальные настройки в php.ini.

Затем, вы устанавливаете и запускаете некую программу-демон, которая называется Agent. При установке прописываете свои уникальные значения server-id и server-token в специальный конфигурационный файл (их можно посмотреть в личном кабинете). Лля Любители контейнеров есть готовый docker image содержащий Agent.

Ещё вам понадобится некая программа клиент (консольная утилита blackfire), которую нужно сконфигурировать при первом запуске, указав свои уникальные значения client-id и client-token (их опять же можно посмотреть в личном кабинете).

Наконец, для удобства, рекомендуется установить специальное расширение для Google Chrome, которое называется Companion.

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

Теперь, когда всё установлено и настроено, процесс выглядит так: открываем страницу тестируемого приложения в браузере Chrome на том сервере, на котором установлено расширение Probe и запущен Agent, например, на localhost. В верхнем правом углу браузера будет иконка Companion, которая открывает меню с большой красной кнопкой «Profile!». При нажатии этой кнопки запускается рендеринг текущей страницы на сервере под профилировщиком Blackfire несколько раз (по умолчанию 10 раз), PHP расширение Probe собирает метрики, а демон-Agent отправляет их на сервис blackfire.io. Как только десяток вызовов прошел, метрики собраны, мы можем увидеть небольшую панель с результатом поверх сайта: общее время выполнения, время затраченное на ожидание ввода-вывода, время на вычисления на CPU, максимальное потребление памяти, сетевой трафик (в том числе трафик между PHP и СУБД), количество внешних http запросов и общее время их выполнения (если ваше приложение обращается к каким-то сервисам по http), количество SQL запросов и время их выполнения.

Рядом с этими краткими значениями есть ссылка «View Profile», которая ведёт на сайт blackfire.io с подробной информацией.

На сайте можно посмотреть граф вызовов функций в виде графического представления в виде квадратиков и стрелочек, причём самые горячие пути и наиболее нагруженные функции выделяются цветом. Подсветку цветом можно показать в нескольких разрезах: самые долгие функции, самые прожорливые по памяти, самые нагруженные по I/O, по процессору или по сетевому трафику.

Помимо графа, можно посмотреть таблицу со списком всех функций и значениями аналогичными Own Time и Time в xdebug — собственное время выполнения и время, включающее все вложенные вызовы. Лично для меня, табличный вид оказался опять же наиболее удобным. Помимо времени выполнения функций в эту таблицу можно вывести и другие метрики: потребление памяти, CPU Time, время на ввод-вывод, объём сетевого трафика.

Отдельным экраном сервис покажет список всех SQL запросов и время выполнения каждого. Эта функциональность напомнила мне другой сервис: New Relic, который тоже крутой, и которым я уже давно пользуюсь в production, но это тема для отдельного подкаста.

Ещё один экран отведён для списка всех внешних HTTP запросов, которые делал сервер в процессе рендеринга страницы.

Таким образом, мы в один клик с помощью расширения для Chrome получили результаты профилирования некоей страницы. А что если нам нужно сделать профиль POST запроса? Или command line скрипта написанного на PHP? Тогда нам понадобится консольная утилита blackfire – она умеет делать как GET, так и POST запросы, умеет профилировать отдельные консольные php скрипты.

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

Удобный просмотр результатов непосредственно на сайте и расширенный набор метрик (по сравнению с Xdebug) – это лишь вершина айсберга. Вся сила Balckfire в автоматизации профилирования.

Во-первых, есть некий PHP SDK, т.е. набор классов, который можно установить через composer и использовать в своём приложении, чтобы включать или выключать профилирование некоторых участков кода на своё усмотрение.

Во-вторых, этот SDK интегрируется с PHPUnit тестами с помощью специально специального трейта, после чего можно писать unit тесты производительности. Задаём желаемые метрики: следующий код должен быть выполнен не более чем за X секунд и он должен сделать не более N числа SQL запросов — мы получили Unit тест на производительность.

В-третьих, в корень проекта можно положить YAML файл с описанием желаемых метрик в зависимости url запроса и при последующих запусках профилировщика через Chrome расширение Companion или через консольную утилиту blackfire, в результатах мы увидим какие метрики превысили желаемый порог. Например, я могу описать в YAML файле, что страница Home должна делать 0 sql запросов, а страница HeavyReport укладываться в 1 секунду. Позже, если я в какой-то момент запущу профилирования страницы HeavyReport, то в результатах появится специальная иконка, сигнализирующая о провале, если рендринг HeavyReport не уложился в заданную секунду. Таким образом можно описать все разделы нашего приложения или сайта, задав желаемые метрики на разные url по маске.

В-четвёртых, запуск тестов производительности можно автоматизировать, и, если какие-то метрики провалены, вы получите уведомление по email, в Slack, HipChat, GitHub, GitLab и др. Я с этим пока не успел разобраться и попробовать, но вот как в документации описан один из возможных сценариев интеграции с GitHub:
1. Разработчик из вашей команды делает Pull Request
2. Этот Pull Request автоматически разворачивается на вашем тестовом сервере
3. Ваша система сборки запускает тесты blackfire
4. Сервис blackfire, получив результаты тестов, отправляет уведомление на GitHub, которое отображается в Pull Request

Итак, основной упор и вся соль этого сервиса – это не разовые замеры ручным запуском через расширение в Chrome, а автоматизация тестирования производительности и интеграция в систему сборки. Что, конечно, потребует дополнительных усилий по настройке, описанию желаемых метрик и написанию дополнительных unit тестов, особенно на начальном этапе.

Отдельным разделом на сайте идёт сервис Blackfire for Magento – набор специфичных метрик и рекомендаций для системы электронной коммерции Magento.

Выглядит красиво, мощно, и даже незаменимо для больших проектов! И, естественно, такой сервис не бесплатен.

Существует несколько тарифных планов.

1. Бесплатных план под названием «Hack» — покажет метрики по времени выполнения и по потреблению памяти. Соответственно, не покажет сетевые запросы, SQL запросы, ввод-вывод, CPU Time. Нет возможности интегрироваться с внешними сервисами для автоматических уведомлений, типа Slack или GitHub.

2. План «Profiler» за 19,90 Евро в месяц. Добавляет недостающие метрики сетевых и SQL запросов, время ввода-вывода, CPU Time, трафик. Даёт автоматические рекомендации по улучшению кода. На самом деле, все рекомендации, которые мне пока выдавались, реально оказались бесполезны, например, «не стоит делать более 5 SQL запросов на страницу».

3. План «Premium» за 82 Евро в месяц. Добавляет возможность описать желаемые метрики в YAML файле в корне проекта, которые будут автоматически проверены при каждом запуске, появляются нотификации и интеграция с внешними сервисами, возможность работать в команде до трёх человек.

4. План «Enterprise» — для получения цены, нужно связаться с отделом продаж. Здесь появляется возможность установить сервис на свои сервера (т.е. не использовать SaaS blackfire.io), приоритетная поддержка, неограниченное число участников в команде.

Как видим, бесплатный сервис даёт нам уровень профилирования примерно как с Xdebug, зато в красивой обёртке и с возможностью запускать на production сервере. Из минусов, сравнивая с тем же Xdebug, при просмотре результатов последнего в PhpStorm, можно быстро переходить к исходникам методов. С веб-обёрткой blackfire.io быстро перейти к исходникам не получится. Другой минус: требуемое PHP расширение Blackfire Probe конфликтует с Xdebug, т.е. я не могу подключить их одновременно на своём localhost.

Вся соль по автоматизации, интеграции, и уведомлений начинается в тарифном плане «Premium» за 82 Евро в месяц.

Я пока продолжаю экспериментировать в рамках пробного периода с доступом ко всем фичам плана «Premium», а потом мне видится следующая схема использования с бесплатным тарифным планом: на локальной машине я отключу расширение Blackfire Probe, т.к. мне всегда под рукой нужна отладка с Xdebug. Скорее всего, я установлю расширение Blackfire Probe на тестовый сервер, где запускаются unit тесты и напишу ряд тестов измеряющих производительность. Время от времени, Blackfire также будет полезен для наглядного сравнения нескольких вариантов кода, т.е. нескольких профилей – это гораздо удобнее и нагляднее, чем сравнивать профили от Xdebug через PhpStorm.

Про Blackfire на этом пока всё.

Анонс: 17 ноября 2016 года (четверг) в офисе Rambler в Москве пройдёт 10-я встреча PHP и Symfony разработчиков Symfony Moscow Meetup – Symfoniacs. В программе доклады про разработку рекламных технологий в Rambler, про DevOps, про архитектуру Symfony 3 и применение паттернов проектирования, про Symfony и RabbitMQ.

Вход бесплатный, но обязательна предварительная регистрация на timepad, начало сбора в 18:30.

До встречи!

  • Джесси Ливермор

    Ура! Новые выпуски!

  • Arthur Edamov

    А вы пропобовали включать blackfire для всех подряд запросов? У меня получается только для единичных запросов из консоли. Хочу запустить нагрузочное и чтобы все запросы в это время профилировались.