Нечетные колебания производительности с php-fpm и nginx
Я запускаю нагрузочный тест на веб-сервис. Это php-приложение, работающее на php-fpm и nginx с fastcgi. Бэкэнд MySQL используется только для небольших операций чтения.
Неизменно, я вижу специфическую закономерность: производительность стабильна и предсказуемо возрастает по мере увеличения трафика, но затем она становится нестабильной в самый разгар: загрузка ЦП постоянно колеблется.
Вот образец производительности, который я вижу (визуализируется с nmon
):
Выпадение всегда совпадает с короткой паузой, которую имеет мой инструмент нагрузочного тестирования - locust.io - когда он заканчивает наращивать до пикового уровня, который я установил для теста.
Моя гипотеза: в этот краткий момент php-fpm
мастер думает, что груз исчез и начинает убивать рабочих; он не может реагировать достаточно быстро, когда движение возвращается полным ходом через мгновение.
Чего я не совсем понимаю, так это того, почему он так и не смог вернуться к нему: я вижу это колебание на неопределенный срок для всех 4 серверов приложений за балансировщиком нагрузки.
Вот мой конфиг пула php-fpm:
[www]
user = www-data
group = www-data
listen = /var/run/php5-fpm.sock
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 100
pm.start_servers = 40
pm.min_spare_servers = 40
pm.max_spare_servers = 100
pm.max_requests = 10000
Я уже подтвердил, что это не проблема с базой данных - я увидел точно такое же поведение после удвоения числа MySQL-ведомых.
Чем это вызвано? Как я могу это остановить?
РЕДАКТИРОВАТЬ:
Вот график, который демонстрирует то, что я вижу. Обратите внимание, что частота отказов, как правило, возрастает так же, как пик user_count, и постепенно снижается.
2 ответа
Как насчет управления памятью? В последние недели я провел несколько тестов по симлару и довел один сервер до предела. Я видел много изменений в памяти. В моем случае огромное количество данных было занесено в своп вместо оперативной памяти для обработки нагрузки. После одного теста у меня был действительно странный результат, ОЗУ больше не использовалось, и все было утеряно. Возможно, это замедляет следующие запросы.
Это пример изображения, как выглядел мой своп после нагрузочного теста
Что происходит с дисковым вводом-выводом и блокировкой? Предположительно, если ваш процесс связан с процессором до точки, где это изменяется, тогда что-то еще занято, и это, скорее всего, ваш диск.
Достигаете ли вы ограничений памяти, из-за которых вы начинаете менять местами? Сколько оперативной памяти используют ваши процессы PHP (RSS)? Сколько оперативной памяти у вас есть в наличии? Получаете ли вы аналогично колеблющуюся производительность, если отбрасываете число процессов PHP? На каком уровне появляется колебание?
Обратите внимание, что pm.max_children = 100
вероятно, слишком высоко. Если вы не имеете дело с долгосрочными запросами, такими как большие загрузки, вам, вероятно, лучше уменьшить его. Я не решаюсь указывать число, не зная, что делает система, но, вероятно, что-то в диапазоне 5-40 будет работать намного лучше. pm.max_requests также, вероятно, будет слишком высоким. Вы, вероятно, обнаружите, что вы получаете небольшую выгоду и, скорее всего, значительное ухудшение, если оно превышает 100 или около того, и если то, что запускается php, сильно изменчиво и потребляет память, или у вас есть утечки памяти, то вы будете лучше уменьшая это немного дальше. Если вы действительно не знаете, что работает, начните с каждой из этих настроек около 30 и экспериментируйте.
PHP генерирует сессии? Как они хранятся? Если они находятся в файловой системе, то что это за файловая система? В некоторых случаях вы получаете узкое место с блокировкой каталога, в котором они находятся. Использование хешированной структуры каталогов для них или использование, например, memcached может помочь в этом.
На что работа strace с отчетом о процессах PHP занимает много времени? Вы можете посмотреть на это с помощью составной команды по следующим направлениям:
(ps wwaux | grep '^www-data.*php' | awk '{print $2}' \
| xargs -n 1 -P 32 strace -r -p ) 2>&1
| perl -ne '($n) = /^ *(\d*\.\d*)/; print "$n\t$_" if ((defined $n) and ($n > 0.01))'