PHP долго работает процесс с 'в', действуя очень странно
Во-первых, я далек от эксперта по Linux, поэтому здесь может быть проблема, но в любом случае, проблема:
Я следовал тому, что написано здесь: http://symcbean.blogspot.com/2010/02/php-and-long-running-processes.html
запустить длительный процесс PHP. Это работает безупречно в моей конфигурации MAMP на моем Mac. Однако, как только я развернул его на нашем VPS, я получил действительно странные результаты.
Итак, сначала я делаю простой тест, используя соединение SSH:
echo '/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;' | at now + 2minutes
Результат:
warning: commands will be executed using /bin/sh
job 2300 at 2012-04-29 19:24
и действительно, через 2 минуты скрипт php выполняется. Все идет нормально.
Далее я попробую следующий подход:
в моем браузере я открываю:
www.myserver.com/Update/LaunchUpdates.php
этот скрипт php содержит строку:
exec("echo '/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;' | at now + 2minutes");
Происходит следующее: я проверяю при -l состояние и вижу:
job 2304 at 2012-04-29 19:32
Затем я жду 2 минуты и снова запускаю на -l. Я ожидаю увидеть пустой результат, но вместо этого я получаю:
job 2305 at 2012-04-29 19:34
и через 2 минуты я получаю
job 2306 at 2012-04-29 19:36
У меня нет ни малейшего представления о том, что там происходит. Сценарий php не выполняется, и задание, похоже, перенесено через 2 минуты. И так будет продолжаться до тех пор, пока я не займусь работой.
Кто-нибудь знает, что может происходить?
Еще немного информации:
cat /etc/*-release
Gentoo Base System version 1.6.14
Еще несколько деталей. Вот содержимое задания at, когда оно запланировано: (at -c [ID])
#!/bin/sh
# atrun uid=1002 gid=100
# mail user 1
umask 33
SERVER_SIGNATURE=\<address\>Apache/2.2.20\ \(Unix\)\ mod_ssl/2.2.20\ OpenSSL/0.9.8o\ Server\ at\ xxx.yyyyy.com\ Port\ 80\</address\>"
"; export SERVER_SIGNATURE
HTTP_USER_AGENT=Mozilla/5.0\ \(Macintosh\;\ Intel\ Mac\ OS\ X\ 10_7_3\)\ AppleWebKit/534.55.3\ \(KHTML,\ like\ Gecko\)\ Version/5.1.5\ Safari/534.55.3; export HTTP_USER_AGENT
HTTP_HOST=xxx.yyyyy.com; export HTTP_HOST
SERVER_PORT=80; export SERVER_PORT
DOCUMENT_ROOT=/home/user/www; export DOCUMENT_ROOT
SCRIPT_FILENAME=/home/user/www/Update/LaunchUpdates.php; export SCRIPT_FILENAME
REQUEST_URI=/Update/LaunchUpdates.php; export REQUEST_URI
SCRIPT_NAME=/Update/LaunchUpdates.php; export SCRIPT_NAME
HTTP_CONNECTION=keep-alive; export HTTP_CONNECTION
REMOTE_PORT=36291; export REMOTE_PORT
PATH=/bin:/usr/bin; export PATH
PWD=/home/user/www/Update; export PWD
SERVER_ADMIN=webmaster@abcdef.com; export SERVER_ADMIN
REDIRECT_STATUS=200; export REDIRECT_STATUS
HTTP_ACCEPT_LANGUAGE=en-us; export HTTP_ACCEPT_LANGUAGE
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml\;q=0.9,\*/\*\;q=0.8; export HTTP_ACCEPT
REMOTE_ADDR=83.101.41.41; export REMOTE_ADDR
SHLVL=764; export SHLVL
SERVER_NAME=xxx.yyyyy.com; export SERVER_NAME
SERVER_SOFTWARE=Apache/2.2.20\ \(Unix\)\ mod_ssl/2.2.20\ OpenSSL/0.9.8o; export SERVER_SOFTWARE
QUERY_STRING=; export QUERY_STRING
SERVER_ADDR=1.2.3.4; export SERVER_ADDR
GATEWAY_INTERFACE=CGI/1.1; export GATEWAY_INTERFACE
SERVER_PROTOCOL=HTTP/1.1; export SERVER_PROTOCOL
HTTP_ACCEPT_ENCODING=gzip,\ deflate; export HTTP_ACCEPT_ENCODING
REQUEST_METHOD=GET; export REQUEST_METHOD
cd /home/user/www/Update || {
echo 'Execution directory inaccessible' >&2
exit 1
}
/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;
При ожидании переназначения задания через 2 минуты я получаю содержимое нового задания, и оно идентично, за исключением:
SHLVL = 764, который стал SHLVL=765
Больше информации!
Как предположил пользователь, я попытался использовать nohup вместо at. Так что я сделал следующее:
Сгенерируйте команду для запуска nohup в файле.sh (с разрешениями на выполнение). а затем выполните exec('nohup .....')
Я также добавил проверку в LaunchUpdates, чтобы удостовериться, что он не вызывается снова, прежде чем пакет nohup завершит работу (я в основном запускаю файл.sh и конец его пакета, а в LaunchUpdates проверяю наличие этого файла).
Короче говоря.
batchProcess.sh содержит:
/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;
rm /home/user/batchProcess.sh
мой код php LaunchUpdates содержит:
$batchFile = "/home/user/batchProcess.sh";
if (file_exists($batchFile))
{
echo 'Process still running. Try again later!';
exit;
}
exec('nohup /home/user/batchProcess.sh > ~/process.out 2> ~/process.err < /dev/null &');
Нет, что происходит:
Я закомментировал строку exec в моем php-скрипте, чтобы файл не выполнялся, а создавался. Я проверяю файл вручную, войдя в систему с помощью ssh, изменив пользователя user и запустив:
nohup /home/user/batchProcess.sh > ~/process.out 2> ~/process.err < /dev/null &
все работает отлично (и.sh файл удаляется в конце)!
Затем я раскомментирую строку exec и перезапущу скрипт php. process.out содержит:
Process still running. Try again later!
Это означает, что он снова выполняет базовый скрипт, а не оператор exec??? Я ПОЛНОСТЬЮ потерялся здесь! Поскольку на обеих учетных записях я запускаю один и тот же скрипт bash, не может быть ошибки относительно того, какие команды выполняются.
Должен ли я начать копаться в логах apache?
Это должно было занять немного времени, мальчик, я был неправ....
9 ответов
Поскольку команда "at" в вашем примере используется для отсоединения скрипта от вызывающего терминала, вы можете использовать "nohup" вместо "at".
Попробуйте это (отсоединитесь от вызывающего процесса, подождите 120 секунд и вызовите php):
/usr/bin/nohup /bin/sleep 120 2> /dev/null && \
/bin/date >> /tmp/longphp.log && \
/usr/local/php53/bin/php -d memory_limit=512M \
-q /home/user/www/Update/Update.php >> /tmp/longphp.log 2>> /tmp/longphp.log
Он создает файл /tmp/longphp.log для анализа. Вы также можете создать оболочку оболочки, содержащую предыдущий скрипт, для упрощения.
Первая попытка: как говорили другие, LaunchUpdates.php
(который вы вызываете из браузера) должен вызывать сам себя. Это, вероятно, не вызывает .../Update.php
как вы сообщаете и намерены, но ... /LaunchUpdates.php
, Легкую ошибку сделать и упустить из виду, поэтому мои деньги на это. [Редактировать: это не было проблемой.]
Вторая попытка: теперь, когда вы добавили сгенерированный at
сценарий, мы можем видеть, что вы на самом деле звоните Update.php
, Следующая подсказка: подоболочка обычно увеличивает переменную SHLVL
на единицу больше, чем значение его родителя. Это чрезвычайно необычно для него выходить за пределы одной цифры, и в этом случае это показывает, что у вас есть цепочка из более чем 700 команд, каждая из которых была запущена предыдущей. Это исключает LaunchUpdates.php
как-то запускается через http, так как SHLVL
затем будет сброшен на единицу больше, чем его значение в Apache.
Моя новая догадка: Update.php
как-то выполняет $SCRIPT_NAME
или же $SCRIPT_FILENAME
, которые (как мы видим в сгенерированном скрипте) устанавливаются at
в LaunchUpdates.php
вместо Update.php
, Вы можете проверить, что проблема в Update.php
заменив его пустым файлом или заглушкой, которая просто записывает сообщение в файл: проблема должна исчезнуть.
Скорее всего, причина в этих настройках среды. Если вы не можете понять это, покажите нам код для Update.php
(в упрощенной форме, но убедитесь, что проблема все еще присутствует), чтобы мы все могли взглянуть.
Изменить 2: Итак, вы подтвердили, что LaunchUpdates.php перезапускается. Поскольку он не вызывает сам себя, он должен вызываться Update.php
,
Помимо использования "at -l" для проверки очереди, попробуйте также использовать "at -c [ID]", чтобы увидеть фактическую команду, которую будет выполнять AT. Я думаю, что это поможет диагностировать ошибку.
Я очень подозреваю, что команда at работает сама по себе, поэтому она меняет свое расписание каждые 2 минуты.
Не так много ответов на вопрос "at", но как альтернативу вы можете настроить тайм-аут на запуск через 2 минуты, используя библиотеку prggmr, которая выполнит код, содержащийся в файле Update.php.
require 'path/to/prggmr/src/prggmr.php';
prggmr\timeout(function(){
// Put logic from Update.php HERE
}, 120000);
// note the time is in milliseconds
prggmr\loop();
Чтобы запустить код, вы должны использовать ту же команду, только удалив "at"
exec("echo '/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;'");
Это запустит код в течение этого тайм-аута через 2 минуты и автоматически завершит работу сценария после слов; обратите внимание, что для запуска библиотеки требуется PHP 5.4, и опять же, это всего лишь мысль в качестве альтернативы, если установка 5.4 не вариант, просто проигнорируйте это.
Я предполагаю, что вы вошли в систему как root (или PHP uid работает как на веб-сервере) при запуске at -l
и что задания связаны с uid PHP работает как на веб-сервере?
Вполне возможно, что кто-то другой обращается к веб-странице, однако, поскольку новые задания добавляются с тем же интервалом, что и задержка для первоначального запуска, заставляет меня думать, что Update.php может вызывать команду для повторного запуска?
Если вы посмотрите на /var/spool/at/atjobs, вы найдете.SEQ и файлы, похожие на это
-rwx------ 1 sergio at 5077 may 3 17:53 a000010153c71d
Этот файл содержит все переменные окружения, а также команду, которую выполняет atd. Надеюсь это поможет
Автор статьи написал следующий код для осуществления планирования:
print `echo /usr/bin/php -q longThing.php | at now`;
Заставить скрипт показать результаты команды планирования с помощью обратных галочек. Это может дать вам подсказку, если at
дает какой-либо неожиданный вывод... возможно, добавить 2>&1
чтобы увидеть, были ли какие-либо ошибки.
Вы должны сделать свой скрипт, который запускаете сейчас, + 2 минуты, создавая и добавляя файл журнала, например:
file_put_contents(__FILE__.'.log', date('c') . "\n", FILE_APPEND);
Тогда вы можете легко проверить, что происходит. Разница в две минуты позволяет предположить, что она перепланирует себя.
Попробуй это:
exec('/home/user/batchProcess.sh >> ~/process.out 2>&1 | at now &');