Предотвращать скрипт PHP, используя все ресурсы во время его запуска?
У меня есть ежедневное задание cron, которое занимает около 5 минут (он собирает некоторые данные, а затем различные обновления баз). Он работает нормально, но проблема в том, что в течение этих 5 минут сайт полностью не отвечает на любые запросы, HTTP или иным образом.
Казалось бы, скрипт задания cron обрабатывает все ресурсы во время его запуска. Я не мог найти что-либо в документах PHP, чтобы помочь мне здесь - как я могу заставить скрипт знать только использовать, скажем, 50% доступных ресурсов? Я бы предпочел, чтобы он работал в течение 10 минут и чтобы сайт был доступен для пользователей в течение того времени, чем он работал в течение 5 минут и жалобы пользователей о простоях каждый день.
Я уверен, что могу придумать способ настроить сам сервер, чтобы это произошло, но я бы предпочел, если бы был встроенный подход в PHP для решения этой проблемы. Есть?
В качестве альтернативы, как и план B, мы могли бы перенаправить все пользовательские запросы на статическую страницу простоя во время работы сценария (в отличие от того, что происходит сейчас, что является загрузкой страницы неограниченно или в конечном итоге отключается).
Ответы
Ответ 1
Обычный сценарий не может выкачать 100% ресурсов, ресурсы распределяются по процессам. Это может сильно замедлить все, но не блокировать все ресурсы (без каких-либо фанковых вещей). Вы можете получить подсказку, выполнив top -s
в своей командной строке, посмотрите, какой процесс занимает много.
Это приводит к выводу, что что-то блокирует все дальнейшие процессы. Как комментирует Аркаша, есть вероятность, что ваша база данных будет заблокирована. Этот ответ объясняет, какой тип таблицы вы должны использовать; Если вы не установили его в InnoDB, вы, вероятно, хотите, чтобы, по крайней мере, для столов блокировки.
Это также может быть дисковый ввод-вывод, если вы пишете огромные файлы, пытаетесь разбить его на более мелкие операции чтения/записи или попытаться поместить некоторую информацию (например, если это файлы со списками) в вашу базу данных (при условии, что у вас есть место для запасных).
Это также может быть процессор. Чтобы исправить это, вам нужно сделать код более эффективным. Перепроверьте свой код, посмотрите, выполняете ли вы тяжелые операции и пытаетесь сделать их меньше. Обычно вы хотите это как можно быстрее, теперь вы хотите, чтобы они были максимально легкими, это меняет способ написания кода.
Если он все еще блокируется, время отладки. Выключите большую часть кода и проверьте, не происходит ли блокировка. Продолжайте включать код, пока не заметите блокировку. Тогда исправьте это. Постарайтесь выяснить, что вам стоит. Только для нескольких сценариев требуются интенсивные ресурсы, сейчас пора оптимизировать. Один из вариантов может быть разделен на два (или более) шага. Запустите cron, который подготавливает/очищает данные и обрабатывает данные. Они не должны запускаться синхронно, между ними может быть несколько минут.
Если это не вариант, сравните свой код и улучшите как можно больше. Если у вас тяжелый запрос, он может улучшиться, выбрав только идентификатор в тяжелом запросе и используя второй запрос только для извлечения данных. Если вы можете, используйте свою базу данных для фильтрации, сортировки и управления данными, не делайте этого в PHP.
То, что я также реализовал однажды, - это спать каждый N действий.
Если ваш сценарий действительно настолько экстремальный, другое решение может перемещать его до того времени, когда на вашем сайте мало/нет посетителей. Даже если вы удалите узкое место, никто не любит медленный сайт.
И всегда есть возможность увеличить ваше оборудование.
Ответ 2
Это действительно зависит от вашей среды.
Если используется база unix, есть встроенные инструменты для ограничения CPU/приоритета для данного процесса.
Вы можете ограничить только сервер или php, скорее всего, это не то, что вы ищете.
Сначала вы можете отделить свою задачу в отдельном процессе.
Для этого есть popen
, но мне стало намного проще сделать этот процесс скриптом bash. hugetask
его hugetask
для примера.
#!/usr/bin/php
<?php
// Huge task here
Затем для вызова из командной строки (или cron):
nice -n 15 ./hugetask
Это ограничит планирование. Это означает, что он будет низко приоритет задачи против других. Система выполнит эту работу.
Вы также можете называть это из своего php напрямую:
exec("nice -n 15 ./hugetask &");
Использование: nice [OPTION] [COMMAND [ARG]...] Запуск COMMAND с измененной привлекательностью, которая влияет на планирование процесса. Без COMMAND напечатайте текущую приятность. Значения ниши варьируются от -20 (наиболее благоприятный для процесса) до 19 (наименее благоприятный для процесса).
Чтобы создать лимит cpu, см. Инструмент cpulimit
который имеет больше опций.
Это сказало, как правило, я просто помещаю некоторый usleep()
в свои скрипты, чтобы замедлить его и избежать создания последовательности данных. Это нормально, если вы используете циклы в своем скрипте. Если вы замедляете выполнение своей задачи за 30 минут, проблем не будет.
См. Также proc_nice
http://php.net/manual/en/function.proc-nice.php
proc_nice() изменяет приоритет текущего процесса на сумму, указанную в приращении. Положительный прирост уменьшит приоритет текущего процесса, тогда как отрицательный прирост повысит приоритет.
И sys_getloadavg
также может помочь. Он вернет массив загрузки системы за последние 1,5 и 15 минут. Его можно использовать в качестве условия теста перед запуском огромной задачи. Или зарегистрировать среднее, чтобы найти лучшее дневное время для запуска огромной задачи. Это может быть подозрительно!
print_r(sys_getloadavg());
http://php.net/manual/en/function.sys-getloadavg.php
Ответ 3
Вы не указываете, какие ресурсы являются вашим узким местом; CPU, память или диск ввода/вывода.
Однако, если это процессор или память, вы можете сделать что-то в этом скрипте: http://php.net/manual/en/function.sys-getloadavg.php http://php.net/manual/en/function.memory -get-usage.php
$yourlimit = 100000000;
$load = sys_getloadavg();
if ($load[0] > 0.80 || memory_get_usage() > $yourlimit) {
sleep(5);
}
Еще одна задача - установить приоритет процесса в вашем скрипте. Это требует SU, хотя это должно быть хорошо для кроны? http://php.net/manual/en/function.proc-nice.php
proc_nice(50);
Я сделал быстрый тест для обоих, и это работает как шарм, спасибо, что попросил, чтобы у меня была такая же кронячка, и это будет реализовано. Похоже, что proc_nice будет работать нормально.
Мой тестовый код:
proc_nice(50);
$yourlimit = 100000000;
while (1) {
$x = $x+1;
$load = sys_getloadavg();
if ($load[0] > 0.80 || memory_get_usage() > $yourlimit) {
sleep(5);
}
echo $x."\n";
}
Ответ 4
Вы можете попытаться отложить выполнение спящего режима. Просто заставьте ваш сценарий приостановить несколько обновлений вашей базы данных.
sleep(60); // stop execution for 60 seconds
Хотя это зависит от процесса, который вы делаете в своем скрипте. Может быть, или не полезно в вашем случае. Стоит попробовать, чтобы вы могли
- Разделите свои запросы
- делать обновления в шагах со сном между ними
Рекомендации
Использование сна для процесса cron
Я не мог описать это лучше, чем цитата из приведенного выше ответа:
Возможно, вы заходите в базу данных из 9 000 000 названий книг и обновляете около 10% из них. Этот процесс должен запускаться в середине дня, но есть так много обновлений, которые нужно выполнить, чтобы запустить пакетную программу, перетащив сервер базы данных на сканирование для других пользователей.
Так что измените пакетный процесс, чтобы отправить, скажем, 1000 обновлений, а затем спящий на 5 секунд, чтобы дать серверу базы данных возможность завершить обработку любых запросов от других пользователей, которые выполнили резервное копирование.
Ресурсы сна и сервера
ресурсы сна зависят от ОС
добавление сна к серверным ресурсам allevaite
Ответ 5
Вероятно, чтобы свести к минимуму использование памяти, вы должны обрабатывать тяжелые и длительные операции партиями. Если вы запрашиваете базу данных с помощью ORM, например, доктрины, вы можете легко использовать существующие функции
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/batch-processing.html
Ответ 6
Трудно сказать, в чем именно проблема может быть, если вы не посмотрите на свой код (cron script). Но чтобы подтвердить, что проблема вызвана заданием cron, вы можете запустить скрипт вручную и проверить реакцию на веб-сайт. Если вы заметили, что сайт запущен при запуске задания cron, нам нужно будет взглянуть на ваш скрипт, чтобы придумать решение.
Ответ 7
Многие циклы в вашем скрипте cron могут потреблять много ресурсов ЦП. Чтобы предотвратить это и уменьшить использование ЦП, просто добавьте некоторые задержки в свой скрипт, например:
while($long_time_condition) {
//Do something here
usleep(100000);
}
В основном, вы даете процессору некоторое время, чтобы сделать что-то еще. Также вы можете использовать функцию proc_nice() для изменения приоритета процесса. Например proc_nice(20);//very low priority
. Посмотрите на этот вопрос.
Если вы хотите найти узкие места в своем коде, вы можете попробовать использовать профайлер Xdebug.
Просто настройте его в своей среде разработчиков, запустите cron вручную, а затем профилируйте любую страницу. Также вы можете профилировать свой cron-скрипт, а также php -d xdebug.profiler_enable=On script.php
, посмотрите на этот вопрос.
Если вы подозреваете, что база данных является вашим узким местом, чем импортировать довольно большой набор данных (или всю базу данных) в вашу локальную базу данных и повторять шаги, регистрируя и проверяя все запросы.
Альтернативно, если это возможно, установите Xdebug на промежуточном сервере, где сервер максимально приближен к производству и профиль страницы во время выполнения cron.