PHP присутствует повсюду и, возможно, является языком, наиболее широко используемым в сети Интернет. Однако он не особо известен своими высокопроизводительными возможностями, особенно когда речь идет о высококонкурентных системах. И именно по этой причине для таких специализированных случаев использования на смену приходят такие языки, как Node (да, я знаю, это не язык), Go и Elixir.
Тем не менее, вы можете МНОГОЕ сделать, чтобы улучшить производительность PHP на вашем сервере. В этой статье основное внимание уделяется php-fpm
стороне дела, которая является естественным способом настройки на вашем сервере, если вы используете Nginx.
Если вы знаете, что php-fpm
это такое, смело переходите к разделу об оптимизации.
Не многие разработчики интересуются аспектами DevOps , и даже среди тех, кто интересуется, очень немногие знают, что происходит под капотом. Интересно, что когда браузер отправляет запрос на сервер, на котором работает PHP, не PHP формирует точку первого контакта; вместо этого это HTTP-сервер, основными из которых являются Apache и Nginx. Затем эти «веб-серверы» должны решить, как подключиться к PHP, и передать ему тип запроса, данные и заголовки.
В современных PHP-приложениях приведенная выше часть «найти файл» — это файл index.php
, которому сервер настроен делегировать все запросы.
Теперь вопрос о том, как именно веб-сервер подключается к PHP, изменился, и эта статья была бы очень длинной, если бы мы вникли во все подробности. Но, грубо говоря, в то время, когда Apache доминировал в качестве предпочтительного веб-сервера, PHP был модулем, включенным в состав сервера.
Таким образом, всякий раз, когда был получен запрос, сервер запускал новый процесс, который автоматически включал PHP, и выполнял его. Этот метод назывался mod_php
сокращением от «PHP как модуль». У этого подхода были свои ограничения, которые Nginx преодолел с помощью php-fpm
.
Ответственность php-fpm
за управление PHP лежит на программе PHP на сервере. Другими словами, веб-сервер (в нашем случае Nginx) не заботится о том, где находится PHP и как он загружен, главное, чтобы он знал, как отправлять и получать от него данные. Если хотите, в этом случае вы можете думать о PHP как о еще одном сервере, который управляет некоторыми дочерними процессами PHP для входящих запросов (то есть у нас есть запрос, доходящий до сервера, который был получен сервером и передан на сервер). — довольно безумно! :-P).
Если вы выполнили какие-либо настройки Nginx или даже просто залезли в них, вы столкнетесь с чем-то вроде этого:
location ~ .php$ { try_files $uri =404; fastcgi_split_path_info ^(.+.php)(/.+)$; fastcgi_pass unix:/run/php/php7.2-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }
Нас интересует строка: fastcgi_pass unix:/run/php/php7.2-fpm.sock;
, которая сообщает Nginx взаимодействовать с процессом PHP через сокет с именем php7.2-fpm.sock
. Итак, для каждого входящего запроса Nginx записывает данные через этот файл и, получив выходные данные, отправляет их обратно в браузер.
Еще раз должен подчеркнуть, что это не самая полная и точная картина происходящего, но она вполне точна для большинства задач DevOps.
Помимо этого, давайте подведем итог тому, что мы узнали на данный момент:
Или графически:
Пока все отлично, но теперь возникает вопрос на миллион долларов: что такое PHP-FPM?
Часть «FPM» в PHP означает «Менеджер быстрых процессов», что является просто причудливым способом сказать, что PHP, работающий на сервере, — это не отдельный процесс, а скорее несколько процессов PHP, которые порождаются, контролируются и уничтожаются. выключен этим менеджером процессов FPM. Именно этому менеджеру процессов веб-сервер передает запросы.
PHP-FPM сам по себе представляет собой целую кроличью нору, поэтому не стесняйтесь исследовать его, если хотите, но для наших целей этого подробного объяснения вполне достаточно.
Так зачем беспокоиться обо всех этих танцах, когда все идет хорошо? Почему бы просто не оставить все как есть.
По иронии судьбы, именно такой совет я даю для большинства случаев использования. Если ваша установка работает нормально и не имеет исключительных случаев использования, используйте значения по умолчанию. Однако, если вы хотите масштабироваться за пределы одной машины, то очень важно выжать максимум из одной машины, поскольку это может сократить счета за сервер вдвое (или даже больше!).
Еще следует понимать, что Nginx был создан для обработки огромных рабочих нагрузок. Он способен обрабатывать тысячи соединений одновременно, но если то же самое не относится к вашей настройке PHP, вы просто потратите ресурсы, поскольку Nginx придется ждать, пока PHP завершит текущий процесс и примет Далее, категорически отрицаем любые преимущества, ради которых был создан Nginx!
Итак, разобравшись с этим, давайте посмотрим, что именно мы бы изменили при попытке оптимизировать php-fpm
.
Расположение файла конфигурации php-fpm
на сервере может отличаться, поэтому вам придется провести некоторое исследование, чтобы найти его. Вы можете использовать команду find в UNIX. В моей Ubuntu путь — /etc/php/7.2/fpm/php-fpm.conf
. Конечно, 7.2 — это версия PHP, которую я использую.
Вот как выглядят первые несколько строк этого файла:
;;;;;;;;;;;;;;;;;;;;; ; FPM Configuration ; ;;;;;;;;;;;;;;;;;;;;; ; All relative paths in this configuration file are relative to PHP's install ; prefix (/usr). This prefix can be dynamically changed by using the ; '-p' argument from the command line. ;;;;;;;;;;;;;;;;;; ; Global Options ; ;;;;;;;;;;;;;;;;;; [global] ; Pid file ; Note: the default prefix is /var ; Default Value: none pid = /run/php/php7.2-fpm.pid ; Error log file ; If it's set to "syslog", log is sent to syslogd instead of being written ; into a local file. ; Note: the default prefix is /var ; Default Value: log/php-fpm.log error_log = /var/log/php7.2-fpm.log
Несколько вещей должны быть сразу очевидны: строка pid = /run/php/php7.2-fpm.pid
сообщает нам, какой файл содержит идентификатор процесса php-fpm
.
Мы также видим, /var/log/php7.2-fpm.log
где php-fpm
будут храниться логи.
Внутри этого файла добавьте еще три переменные, например:
emergency_restart_threshold 10 emergency_restart_interval 1m process_control_timeout 10s
Первые две настройки носят предостерегающий характер и сообщают процессу php-fpm
, что если десять дочерних процессов завершатся сбоем в течение минуты, основной php-fpm
процесс должен перезапуститься.
Это может показаться ненадежным, но PHP — это недолговечный процесс, из-за которого происходит утечка памяти, поэтому перезапуск основного процесса в случае серьезного сбоя может решить множество проблем.
Третий вариант, process_control_timeout
, сообщает дочерним процессам, что им следует подождать определенное время перед выполнением сигнала, полученного от родительского процесса. Это полезно в тех случаях, когда дочерние процессы находятся в середине чего-то, когда родительские процессы, например, отправляют сигнал KILL. Имея в запасе десять секунд, у них будет больше шансов завершить свои дела и изящно уйти.
Удивительно, но это не суть php-fpm
конфигурации! Это связано с тем, что для обслуживания веб-запросов php-fpm
создается новый пул процессов, который будет иметь отдельную конфигурацию. В моем случае имя пула оказалось таким, www
а файл, который я хотел редактировать, — /etc/php/7.2/fpm/pool.d/www.conf
.
Давайте посмотрим, как начинается этот файл:
; Start a new pool named 'www'. ; the variable $pool can be used in any directive and will be replaced by the ; pool name ('www' here) [www] ; Per pool prefix ; It only applies on the following directives: ; - 'access.log' ; - 'slowlog' ; - 'listen' (unixsocket) ; - 'chroot' ; - 'chdir' ; - 'php_values' ; - 'php_admin_values' ; When not set, the global prefix (or /usr) applies instead. ; Note: This directive can also be relative to the global prefix. ; Default Value: none ;prefix = /path/to/pools/$pool ; Unix user/group of processes ; Note: The user is mandatory. If the group is not set, the default user's group ; will be used. user = www-data group = www-data
; Пользователь Unix/группа процессов ; Примечание: Пользователь является обязательным. Если группа не задана, используется группа пользователей по умолчанию. ; будет использован. пользователь = www-данные группа = www-данные
Беглый взгляд на конец приведенного выше фрагмента решает загадку того, почему серверный процесс запускается как www-data
. Если вы столкнулись с проблемами прав доступа к файлам при настройке своего веб-сайта, вы, вероятно, изменили владельца или группу каталога на www-data
, что позволило процессу PHP иметь возможность записывать файлы журналов, загружать документы и т. д.
Наконец мы подходим к источнику проблемы — настройке диспетчера процессов (pm). Обычно вы увидите значения по умолчанию примерно так:
pm = dynamic pm.max_children = 5 pm.start_servers = 3 pm.min_spare_servers = 2 pm.max_spare_servers = 4 pm.max_requests = 200
Итак, что же здесь означает « динамичный »? Я думаю, что официальные документы лучше всего объясняют это (я имею в виду, что это уже должно быть частью файла, который вы редактируете, но я воспроизвел это здесь на тот случай, если это не так):
; Choose how the process manager will control the number of child processes. ; Possible Values: ; static - a fixed number (pm.max_children) of child processes; ; dynamic - the number of child processes are set dynamically based on the ; following directives. With this process management, there will be ; always at least 1 children. ; pm.max_children - the maximum number of children that can ; be alive at the same time. ; pm.start_servers - the number of children created on startup. ; pm.min_spare_servers - the minimum number of children in 'idle' ; state (waiting to process). If the number ; of 'idle' processes is less than this ; number then some children will be created. ; pm.max_spare_servers - the maximum number of children in 'idle' ; state (waiting to process). If the number ; of 'idle' processes is greater than this ; number then some children will be killed. ; ondemand - no children are created at startup. Children will be forked when ; new requests will connect. The following parameter are used: ; pm.max_children - the maximum number of children that ; can be alive at the same time. ; pm.process_idle_timeout - The number of seconds after which ; an idle process will be killed. ; Note: This value is mandatory.
Итак, мы видим, что возможны три значения:
php-fpm
будут поддерживаться в любой момент времени.Итак, какое значение имеют эти настройки?
Проще говоря, если у вас веб-сайт с низким трафиком, «динамическая» настройка в большинстве случаев является пустой тратой ресурсов. Если вы pm.min_spare_servers
установили значение 3, три процесса PHP будут созданы и поддерживаться, даже если на веб-сайте нет трафика. В таких случаях лучшим вариантом является «по требованию», позволяющий системе решать, когда запускать новые процессы.
С другой стороны, в этом случае будут наказаны веб-сайты, которые обрабатывают большие объемы трафика или должны быстро реагировать. Создание нового PHP-процесса, включение его в пул и его мониторинг — это дополнительные затраты, которых лучше избегать.
Использование pm = static
исправляет количество дочерних процессов, позволяя использовать максимум системных ресурсов для обслуживания запросов, а не для управления PHP. Если вы все-таки пойдете по этому пути, знайте, что на нем есть свои правила и подводные камни. Довольно толстая, но весьма полезная статья об этом есть здесь.
Заключительные слова
Поскольку статьи о веб-производительности могут спровоцировать войны или сбить людей с толку, я считаю, что необходимо сказать несколько слов, прежде чем мы закроем эту статью. Настройка производительности — это не только знание системы, но и догадки и темные искусства.
Даже если вы знаете все php-fpm
настройки наизусть, успех не гарантирован. Если вы понятия не имели о существовании php-fpm
, то вам не нужно тратить время на беспокойство по этому поводу. Просто продолжайте делать то, что вы уже делаете, и продолжайте.
В то же время избегайте того, чтобы стать фанатом производительности. Да, вы можете добиться еще большей производительности, перекомпилировав PHP с нуля и удалив все модули, которые вы не будете использовать, но этот подход недостаточно разумен в производственных средах. Вся идея оптимизации чего-либо состоит в том, чтобы посмотреть, отличаются ли ваши потребности от настроек по умолчанию (что случается редко!), и при необходимости внести незначительные изменения.