Оптимизация производительности
Преждевременная оптимизация — корень всех зол в программировании.
Возможно вы встречали эту цитату и ранее, однако я люблю цитировать её полностью:
Нам следует забывать о небольшой эффективности, например, в 97% случаев: преждевременная оптимизация — корень всех зол. И напротив, мы должны уделить всё внимание оставшимся 3%.
-- Donald Knuth
Даже незначительное увеличение производительности имеет важное значение, особенно для интернет-магазинов. Теперь, когда приложение гостевой книги готово, давайте проверим его производительность.
Лучший способ определить потенциальные области для оптимизации — использовать профилировщик. Одним из наиболее популярных на сегодняшний день является Blackfire (важное примечание: я также основатель проекта Blackfire).
Знакомство с Blackfire
Blackfire состоит из нескольких частей:
- Клиент, запускающий профили (CLI-инструмент Blackfire или расширение для браузера Google Chrome или Firefox);
- Агент, который подготавливает и собирает данные перед их отправкой на сайт blackfire.io для отображения;
- PHP-модуль (зонд), который инструментирует PHP-код.
Зарегистрируйтесь, чтобы начать работу с Blackfire.
Установите Blackfire на вашу локальную машину, запустив скрипт быстрой установки:
1
$ curl https://installer.blackfire.io/installer.sh | bash
Установщик загрузит и установит CLI-инструмент Blackfire.
По завершении устанавливает PHP-зонд на все доступные версии PHP:
1
$ sudo blackfire php:install
И включите PHP-зонд для нашего проекта:
1 2 3 4 5 6 7 8 9 10
--- a/php.ini
+++ b/php.ini
@@ -6,3 +6,7 @@ session.use_strict_mode=On
realpath_cache_ttl=3600
zend.detect_unicode=Off
xdebug.file_link_format=vscode://file/%f:%l
+
+[blackfire]
+# use php_blackfire.dll on Windows
+extension=blackfire.so
Перезапустите веб-сервер, чтобы PHP cмог загрузить Blackfire:
1 2
$ symfony server:stop
$ symfony server:start -d
Для хранения профилей приложений в вашей учётной записи, необходимо настроить инструмент Blackfire CLI, используя ваши персональные клиентские учётные данные. Найдите их в верху страницы Settings/Credentials
и выполните следующую команду, предварительно подставив свои данные в соответствующие места:
1
$ blackfire client:config --client-id=xxx --client-token=xxx
Установка Blackfire-агента в Docker
Служба агента Blackfire уже настроена в стеке Docker Compose:
Для соединения с сервером вам необходимо получить ваши персональные серверные учётные данные. Эти учётные данные указывают, где вы хотите хранить профили, которые вы можете создать для каждого проекта. Их можно найти внизу страницы Settings/Credentials
. Сохраните данные локально в файле .env.local
:
1 2 3 4
$ symfony console secrets:set BLACKFIRE_SERVER_ID
# xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
$ symfony console secrets:set BLACKFIRE_SERVER_TOKEN
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Теперь запустите новый контейнер:
1 2
$ docker compose stop
$ docker compose up -d
Исправление неработающей установки Blackfire
Если во время профилирования появляется ошибка — увеличьте уровень логирования ошибок Blackfire, чтобы собирать больше информации в отчётах об ошибках:
1 2 3 4 5 6 7
--- a/php.ini
+++ b/php.ini
@@ -10,3 +10,4 @@ zend.detect_unicode=Off
[blackfire]
# use php_blackfire.dll on Windows
extension=blackfire.so
+blackfire.log_level=4
Перезапустите веб-сервер:
1 2
$ symfony server:stop
$ symfony server:start -d
И следите за логами:
1
$ symfony server:log
Запустите профилирование снова и проверьте записи лога.
Настройка Blackfire в продакшене
По умолчанию Blackfire добавлен во все проекты на Platform.sh.
Установите учетные данные сервера в качестве секретов для окружения production:
1 2 3 4
$ symfony console secrets:set BLACKFIRE_SERVER_ID --env=prod
# xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
$ symfony console secrets:set BLACKFIRE_SERVER_TOKEN --env=prod
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Зонд PHP уже включён, как и любой другой необходимый модуль PHP:
Настройка Varnish для работы с Blackfire
Перед развёртыванием, чтобы приступить к профилированию, вам необходимо обойти HTTP-кеш Varnish. Если этого не сделать — Blackfire никогда не сможет получить доступ к вашему PHP-приложению. Достаточно разрешить HTTP-запросы профилирования, приходящие только с вашего локального компьютера.
Узнайте ваш текущий IP-адрес:
1
$ curl https://ifconfig.me/
И используйте его для настройки Varnish:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
--- a/.platform/config.vcl
+++ b/.platform/config.vcl
@@ -1,3 +1,11 @@
+acl profile {
+ # Authorize the local IP address (replace with the IP found above)
+ "192.168.0.1";
+ # Authorize Blackfire servers
+ "46.51.168.2";
+ "54.75.240.245";
+}
+
sub vcl_recv {
set req.backend_hint = application.backend();
set req.http.Surrogate-Capability = "abc=ESI/1.0";
@@ -8,6 +16,16 @@ sub vcl_recv {
}
return (purge);
}
+
+ # Don't profile ESI requests
+ if (req.esi_level > 0) {
+ unset req.http.X-Blackfire-Query;
+ }
+
+ # Bypass Varnish when the profile request comes from a known IP
+ if (req.http.X-Blackfire-Query && client.ip ~ profile) {
+ return (pass);
+ }
}
sub vcl_backend_response {
Теперь можно разворачивать.
Профилирование веб-страниц
Для профилирования обычных веб-страниц в Firefox или Google Chrome используйте соответствующие расширения.
Во время профилирования не забудьте отключить HTTP-кеш на вашей локальной машине в файле config/packages/framework.yaml
. Если этого не выполнить, то вместо своего кода, вы будете профилировать слой HTTP-кеша Symfony.
Для получения наиболее полного представления о производительности приложения в продакшене, вам также необходимо профилировать окружение продакшена ("production"). По умолчанию ваше локальное окружение использует среду разработки ("development"), что влечёт за собой существенные накладные расходы (это происходит в основном из-за сбора данных для отладочной панели и Symfony-профилировщика).
Note
Поскольку мы будем профилировать окружение "production", в конфигурации ничего менять не нужно, так как в предыдущей главе мы включили слой HTTP-кеша Symfony только для среды "development".
Переключите окружение вашей локальной машины на работу в продакшене путём изменения значения переменной окружения APP_ENV
в файле .env.local
:
1
APP_ENV=prod
Также вы можете использовать команду server:prod
:
1
$ symfony server:prod
Не забудьте переключиться обратно на окружение разработки после завершения сеанса профилирования:
1
$ symfony server:prod --off
Профилирование API-ресурсов
Профилирование API или SPA лучше выполнять в командной строке с помощью установленного ранее инструмента Blackfire CLI:
1
$ blackfire curl `symfony var:export SYMFONY_PROJECT_DEFAULT_ROUTE_URL`api
Команда blackfire curl
принимает те же аргументы и опции, что и cURL.
Сравнение производительности
В шаге про кеширование для повышения производительности мы добавили слой кеширования, однако мы не проверили, как внесённые изменения повлияли на производительность. Так как довольно сложно угадать, что будет работать быстрее, а что медленнее, то можно оказаться в ситуации, когда из-за оптимизации чего-либо ваше приложение на самом деле станет только медленнее.
С помощью профилировщика всегда необходимо измерять влияние каждой сделанной оптимизации. Blackfire позволяет упростить визуальную оценку производительности благодаря возможности сравнения.
Написание функциональных тестов по принципу чёрного ящика
Мы уже знакомы с тем, как писать функциональные тесты с помощью Symfony. Blackfire, в свою очередь, может быть использован для написания браузерных сценариев, которые можно запускать по желанию с помощью приложения Blackfire player. Давайте напишем сценарий, который отправит новый комментарий и проверит его по ссылке в электронной почте в окружении для разработки, а также в административной панели в продакшене.
Создайте файл .blackfire.yaml
со следующим содержимым:
Загрузите Blackfire player для выполнения сценария на локальной машине:
1 2
$ curl -OLsS https://get.blackfire.io/blackfire-player.phar
$ chmod +x blackfire-player.phar
Запустите этот сценарий в окружении разработки:
1
$ ./blackfire-player.phar run --endpoint=`symfony var:export SYMFONY_PROJECT_DEFAULT_ROUTE_URL` .blackfire.yaml --variable "webmail_url=`symfony var:export MAILER_WEB_URL 2>/dev/null`" --variable="env=dev" -vv
Или в продакшене:
1
$ ./blackfire-player.phar run --endpoint=`symfony cloud:env:url --pipe --primary` .blackfire.yaml --variable "webmail_url=NONE" --variable="env=prod" -vv
Сценарии Blackfire, также могут использовать профили для каждого запроса и запускать тесты производительности, если добавить соответствующий флаг --blackfire
.
Автоматизация проверок производительности
Отслеживание производительности позволяет понять не только как улучшить производительность существующего кода, но также и контролировать её падение, вызванное новыми изменениями.
Написанный в предыдущем разделе сценарий может запускаться автоматически через непрерывную интеграцию или на регулярной основе в продакшене.
На Platform.sh также возможно запускать сценарии при создании новой ветки или развёртывании в продакшене, чтобы автоматически проверять производительность нового кода.