Привет, Хабр. Хочу рассказать историю, как я некоторое время назад занялся профилированием PHP — и куда это меня привело.
Меня зовут Олег, я больше 15 лет пишу код, проектирую системы и руковожу командами. Ещё выступаю на профильных конференциях, таких как Highload++, PHPRussia. Руковожу программным комитетом PHP-секции в рамках конференции “Стачка”.
Тема Observability мне интересна: я постоянно стараюсь тюнить это в проекте, над которым работаю. На текущем месте я развивал всё шаг за шагом — от алертов до трейсов.
Обычно, когда речь заходит о профилировании PHP-приложений, вспоминают проверенные временем инструменты: XHProf, PHP Spy, Xdebug — если говорить про open source. Blackfire, Tideways, NewRelic, Sentry или Datadog — если из коммерческих решений. Как вы заметили, в этом списке нет никакого Excimer. Откуда он взялся и зачем его использовать в современном PHP?
На самом деле, про него мало кто вообще слышал. Я и сам узнал случайно, когда затаскивал трейсинг и профайлинг в наш Sentry, с которым он работает из коробки через SDK. Стало интересно, почему выбор пал именно на такой малоизвестный инструмент — и что ещё с этим можно сделать. Да и вообще: как обстоят дела с профайлингом PHP в 2025 году?
Итак, нам нужно решение, которое:
-
для постоянного сбора данных с прода,
-
имеет возможность отправлять трейсы и профайлы,
-
может связать их через общий trace_id,
-
чтобы было удобно просматривать в едином интерфейсе,
-
и в идеале — заменить хранилище и визуализатор без изменений в коде.
Про последнее сразу вспоминается OpenTelemetry — стандарт для observability. По сути, реализуем общение между нашим бэкендом и инструментом по протоколу.
Коммерческие инструменты: Datadog, Blackfire, Tideways
Прекрасные инструменты. Подходят для продакшена, не нагружают CPU при сборе профайлов, не требуют тонкой настройки. Совместимы с последними версиями PHP.
Конечно, это коммерческие решения, которые стали недоступны для большинства компаний в РФ. Зато предоставляют большой инструментарий для сбора трейсов и профайлов.
Ещё одна задача — возможный переезд с одного инструмента на другой. Поскольку это проприетарные решения, всегда есть риск вендор-лока.
XHProf
До сих пор пользуется популярностью в сообществе. В продакшене стоит использовать с осторожностью: может создавать нагрузку при высоком трафике. Потеря производительности — от 20–30% до 200%.
Кроме того, последний релиз был в 2020 году, а последний коммит — два года назад. Репозиторий не выглядит живым. Но он всё ещё работает!
Формат OpenTelemetry не поддерживается, с трейсами связать тоже не выйдет.
PHP Spy
Работает отдельно от вашего PHP-приложения и собирает метрики с php-fpm. Учитывая новости, что The PHP Foundation взяла под своё крыло альтернативный рантайм FrankenPHP, я думаю, что было бы классно иметь более универсальный инструмент, не зависящий от рантайма.
Ещё одна проблема — связать профайл с трейсом. Передать trace_id наружу? Можно, но задача нетривиальная.
Тем не менее, PHP Spy обладает рядом плюсов:
-
поддерживает OpenTelemetry,
-
достаточно производительный,
-
нет необходимости вмешиваться в код.
К сожалению, его поддержка сейчас оставляет желать лучшего. Но есть живые форки.
Xdebug
Для постоянного профайла в продакшена всерьёз даже не рассматривается 🙂 xDebug 3 версии можно включать через куку без заметных просадок по производительности.
Для локального профайлинга и дебаггинга — незаменимый инструмент.
Excimer?
Это профайлер, который использует и развивает Wikimedia. Написан на C, ставится как расширение PHP и работает в контексте приложения. Оказывает незначительную нагрузку на CPU и отлично подходит для прода.
Его ключевая особенность: использование POSIX таймеров (timer signals) для выборочного сэмплирования стека вызовов.
Особенности Архитектуры
-
Excimer использует setitimer(ITIMER_PROF) или timer_create + SIGPROF.
-
Таймеры запускаются в отдельном потоке ядра и прерывают исполнение PHP с заданным интервалом (—period=0.01 — 100 сэмплов/сек).
-
На каждый сигнал выполняется обработчик, в котором собирается стек вызовов.
И поскольку таймеры POSIX работают на уровне ядра, они практически не замедляют основной поток исполнения.
В момент сигнала SIGPROF Excimer вызывает zend_fetch_debug_backtrace(), чтобы получить текущий стек. Полученные сэмплы складываются в кольцевой буфер в памяти.
Но: не может отправлять данные по стандарту OpenTelemetry. По сути, работает только с Sentry и WikimediaDebug (уверен, про него вы тоже ничего не слышали), причём только в локальном режиме.
Я решил, что было бы классно научить Excimer работать с инструментами, поддерживающими OpenTelemetry — и в целом с чем-то, кроме WikimediaDebug. Так родился мой первый open source проект — excimetry.
Что реализовано на данный момент:
-
Возможность отправлять профайлы в Pyroscope
-
Возможность отправки в формате OpenTelemetry (protobuf)
-
Экспорт профайлов в формате Speedscope
-
Выгрузка просто в файл (в формате любого из exporters)
-
Поддержка профилирования CLI-команд
// Создадим профилировщик, можно передать необходимые конфиги $profiler = new Excimetry\Excimetry(); // начинаем профилирование $profiler->start(); // какой-то код // ... // останавливаем профилирование $profiler->stop();
Далее нужно создать экспортёр и выбрать как будет выполняться отправка.
use Excimetry\Profiler\ExcimerProfiler; use Excimetry\Exporter\CollapsedExporter; use Excimetry\Backend\PyroscopeBackend; $backend = new PyroscopeBackend( serverUrl: 'http://localhost:4040', appName: 'my-application', labels: ['env' => 'production'], exporter: new CollapsedExporter(), ); // Добавим лейблы. Например, trace_id $backend->addLabel('trace_id', 'some_trace_id'); $backend->addLabel('region', 'Saint-P'); // Отправим а Pyroscope $backend->send($profiler->getLog()); // Или то же самое, но асинхронно $backend->setAsync(true); $backend->send($profiler->getLog());
Иногда требуется снять профайл с консольной команды или консольного скрипта. Это тоже возможно:
excimetry-profile \ --period=0.01 \ --mode=wall \ --format=speedscope \ --output=profiles \ path/to/script.php
Несколько месяцев уже у нас работает этот профилировщик продакшене, пробовали отправлять до 30% трафика, на производительности это не сказалось. Сейчас, конечно, сильно меньше, потому что Sentry не справляется с нагрузкой. В планах переехать на Grafana Stack как на более производительный и специализированный.
Так как для меня это первый опыт в open source буду рад обратной связи тут или в issue на github
Конечно, я не могу упустить возможность сказать вам, что много интересного об архитектуре и разработке публикую у себя в telegram канале — https://t.me/ask_for_oleg.
А каким профилировщиком пользуетесь вы? Поделитесь опытом 🙂
ссылка на оригинал статьи https://habr.com/ru/articles/926650/
Добавить комментарий