В журналах многих нагруженных бизнес-сайтов появляются два предупреждения: у Apache "server reached MaxRequestWorkers setting" и у PHP-FPM "server reached pm.max_children setting". Оба означают одно и то же: запросы ждут свободного воркера. Инстинкт подсказывает увеличить число и забыть, но на сервере с ограниченной памятью это меняет медленные страницы на сервер, который уходит в своп или исчерпывает память в самый неподходящий момент. Хорошая производительность Apache достигается подбором MaxRequestWorkers и PHP-FPM под реально имеющуюся RAM. В этом руководстве разбирается такой расчёт для распространённой современной конфигурации: Apache 2.4 с event MPM перед PHP-FPM.

Сначала выясните, какие MPM и обработчик PHP у вас используются

Числа имеют смысл, только когда известна архитектура. apachectl -V показывает активный MPM в строке "Server MPM", а в Debian и Ubuntu a2query -M выводит только его название. В системах на базе Red Hat служба называется httpd, но производительность httpd подчиняется тем же правилам. Затем проверьте, как запускается PHP: через PHP-FPM, обычно по proxy_fcgi, или как mod_php внутри Apache. Разница велика:

  • prefork с mod_php: каждый процесс Apache несёт полный интерпретатор PHP, даже когда просто отдаёт картинку. Расход памяти на соединение высок, а MaxRequestWorkers фактически становится вашим пределом для PHP.
  • Apache event MPM с PHP-FPM: потоки Apache лёгкие и обслуживают соединения, статические файлы и keep-alive, а PHP работает в отдельном пуле процессов. Apache может держать намного больше соединений, а настройкой, защищающей память, становится pm.max_children в PHP-FPM.

Если сервер всё ещё работает на prefork с mod_php, переход на event с PHP-FPM — обычно самое эффективное одиночное изменение. Но его нужно тестировать, потому что строки php_value в файлах .htaccess работают только с mod_php, и их придётся перенести в пул PHP-FPM или файл .user.ini.

Измерьте использование памяти Apache и размер процесса PHP

Расчёт начинается с измерения в обычный напряжённый период, а не тихим воскресным утром. Посмотрите:

  • Общий объём RAM и что ещё живёт на сервере. База данных, кеш вроде Redis, почтовые службы и сама операционная система требуют свою долю раньше, чем что-то достанется веб-воркерам.
  • Средний размер процесса PHP-FPM. ps со столбцом rss, отфильтрованный по процессам php-fpm, даёт рабочую цифру. Берите типичный размер под нагрузкой, а не самый маленький простаивающий процесс.
  • Использование памяти Apache на дочерний процесс. При event MPM оно обычно скромное, потому что потоки внутри каждого дочернего процесса делят память.

Apache mod_status со страницей server-status, доступной только с вашего IP-адреса, показывает scoreboard: сколько воркеров заняты чтением, записью или удержанием keep-alive-соединений. Если большинство из них висит в keep-alive, значит, KeepAliveTimeout занимает слоты ради простаивающих браузеров.

Расчёт PHP-FPM pm.max_children

Для PHP-FPM рабочая формула проста: память, которую можно отдать PHP, делённая на средний размер процесса PHP. Например, на сервере с 8 GB RAM, где базе данных, кешу и системе нужно около 4 GB, для PHP остаётся примерно 4 GB. При около 100 MB на процесс это допускает около 40 дочерних процессов, и разумно начать немного ниже и наблюдать.

Важны ещё две настройки. Режим менеджера процессов: dynamic держит наготове диапазон процессов, ondemand запускает их только при необходимости, что подходит для множества небольших сайтов на одном сервере, а static держит фиксированное число, что подходит для одного нагруженного приложения. И pm.max_requests, который перезапускает каждый процесс после заданного числа запросов, чтобы медленные утечки памяти в плагинах не накапливались. Учтите, что в стандартном файле пула, поставляемом с PHP, pm.max_children равен всего 5, поэтому нагруженный сайт с настройками по умолчанию упирается в предел очень рано.

Расчёт MaxRequestWorkers для event MPM

При event MPM MaxRequestWorkers — это общее число потоков, которые могут одновременно обслуживать запросы, и оно должно укладываться в ServerLimit, умноженный на ThreadsPerChild. Значения Apache, заложенные при компиляции, — 16 процессов по 25 потоков, всего 400, а конфигурационные файлы дистрибутивов часто задают меньшие значения. Поскольку PHP-FPM уже ограничивает работу PHP, MaxRequestWorkers на сервере с event может уверенно находиться выше pm.max_children. Лишние потоки почти не расходуют память и позволяют Apache продолжать отдавать изображения, таблицы стилей и keep-alive-соединения, пока PHP занят. Если вы поднимаете MaxRequestWorkers выше ServerLimit, умноженного на ThreadsPerChild, поднимите и ServerLimit и полностью перезапустите Apache, потому что плавная перезагрузка (graceful reload) не меняет ServerLimit.

При MPM prefork расчёт строже: MaxRequestWorkers, умноженный на средний размер процесса Apache, должен уместиться в память, оставшуюся для Apache, поскольку каждый воркер — полноценный процесс.

KeepAlive, таймауты и проверка результата

  • KeepAliveTimeout. Значение по умолчанию в 5 секунд разумно; длинные значения удерживают слоты для браузеров, которые уже закончили. Event MPM эффективно обслуживает простаивающие keep-alive-соединения, и это ещё одна причина предпочесть его.
  • Timeout. Очень длинный таймаут запроса позволяет нескольким медленным или злонамеренным клиентам занимать воркеры. Подгоните его под самый медленный законный запрос, например большую загрузку или отчёт.
  • По одному изменению за раз. Применяйте каждое изменение плавной перезагрузкой, где это возможно, наблюдайте за памятью, scoreboard и временем ответа в течение напряжённого периода и сохраняйте прежнюю конфигурацию, чтобы можно было откатиться.

Тот же подход «сначала измерить» применим к любому проекту оптимизации Apache, и с него начинается наша настройка производительности Apache. Если страницы остаются медленными, а воркеры простаивают, узкое место в другом месте, обычно в запросах к базе данных или в самом приложении, и здесь помогает настройка MySQL и MariaDB.

Часто задаваемые вопросы: MaxRequestWorkers

Нужно ли просто увеличить MaxRequestWorkers, когда появляется предупреждение?

Только после проверки памяти. Если у сервера есть запас, умеренное увеличение допустимо. Если он уже близок к использованию свопа, более высокий предел ухудшит ситуацию; ответ — меньше запросов и быстрее благодаря кешированию, либо больше памяти.

Сколько RAM нужно сайту на WordPress на один процесс PHP?

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

Безопасен ли event MPM с файлами .htaccess?

Да. MPM не меняет то, как работают правила перезаписи и доступа в .htaccess. Меняется то, как запускается PHP, поэтому нужно проверить, не требуется ли перенести настройки PHP из этих файлов в пул PHP-FPM.

Видите предупреждения MaxRequestWorkers или pm.max_children на сервере в Лос-Анджелесе или Лонг-Бич? Наша услуга оптимизации веб-сервера Apache начинается с измерений, а затем настраивает MPM, PHP-FPM, кеширование и защиту в комплексе. Свяжитесь с нами, прислав предупреждения из журнала ошибок и объём RAM в сервере.

Сколько стоит веб-сервер Apache?

Само ПО бесплатно: Apache HTTP Server — свободное программное обеспечение с открытым исходным кодом, как и PHP-FPM, который поставляется вместе с PHP. На практике стоимость веб-сервера Apache определяется оборудованием и временем вокруг него. Воркеры, рассчитанные сверх доступной RAM, загоняют сервер в своп, а привычный выход — купить машину побольше. Измерение размера процесса PHP, подбор pm.max_children и MaxRequestWorkers и добавление кеширования могут позволить существующему серверу обработать больше трафика, прежде чем понадобится больше памяти.