Я провожу нагрузочный тест для веб-службы. Это приложение 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 достигает пика, и постепенно снижается.
А как насчет вашего управления памятью? В последние недели я провел несколько простых тестов и довел один сервер до предела. Я видел много изменений в памяти. В моем случае огромное количество данных было перенесено в своп вместо оперативной памяти для обработки нагрузки. После одного теста у меня был очень странный результат, оперативная память больше не использовалась, и все полностью ушло в своп. Может это то, что тормозит следующие запросы.
Это пример изображения того, как мой своп выглядел после нагрузочного теста.
Что происходит с дисковым вводом-выводом и блокировкой? Предположительно, если ваш процесс связан с процессором до точки, где это изменяется, значит, что-то еще занято, и, скорее всего, это ваш диск.
Вы достигли пределов памяти, из-за которых вы начали подкачку? Сколько оперативной памяти используют ваши 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))'