Php long running process с "at" действует очень странно

Во-первых, я далек от эксперта 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 script. Пока все хорошо.

Далее я пробую следующий подход:

в моем браузере я открываю:

www.myserver.com/Update/LaunchUpdates.php

этот php script содержит строку:

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 script не выполняется, и работа, похоже, перепланирует себя через 2 минуты. И это продолжается и продолжается, пока я не заработаю работу.

Кто-нибудь знает, что может произойти?

Дополнительная информация:

cat /etc/*-release
Gentoo Base System version 1.6.14

Некоторые подробности. Вот содержимое заданий, когда оно запланировано: (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
[email protected]; 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 вместо. Итак, я сделал следующее:

Сгенерируйте команду, которую должен выполнить nohup в .sh файле (с разрешениями на выполнение). и затем выполните exec ('nohup.....')

Я также добавил проверку в LaunchUpdates, чтобы убедиться, что она не вызывается снова до того, как пакет nohup выполнил запуск (я в основном rm.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, меняя пользовательский "пользователь" и запуская:

nohup /home/user/batchProcess.sh > ~/process.out 2> ~/process.err < /dev/null &

все работает нормально (и файл .sh удаляется в конце)!

Далее я раскомментирую строку exec и запустил PHP скрипт. process.out содержит:

Process still running. Try again later!

Это означает, что он снова выполняет base- script, а не оператор exec??? Я ПОЛНОСТЬЮ потерял здесь! Поскольку на обеих учетных записях я запускаю те же bash script, не может быть ошибки относительно того, какие команды выполняются.

Должен ли я начинать копать в журналах apache?

Предполагалось, что у вас мало времени, мальчик был я неправ.

Ответ 1

Так как команда "at" в вашем примере используется для отсоединения script от вызывающего терминала, вы можете использовать "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 для анализа. Вы также можете создать оболочку оболочки, содержащую предыдущий script, чтобы упростить.

Ответ 2

Я предполагаю, что вы вошли в систему под именем root (или uid PHP работает как на веб-сервере) при запуске at -l и что задания связаны с uid PHP, как на веб-сервере?

Возможно, что кто-то еще имеет доступ к веб-странице, однако, поскольку новые задания добавляются с тем же интервалом, что и задержка для первоначального запуска, заставляет меня думать, что Update.php может вызвать команду для запуска снова?

Ответ 3

Кроме того, чтобы проверить очередь на "-l", попробуйте использовать "at -c [ID]", чтобы увидеть действительную команду, которая будет запущена AT. Я думаю, что это поможет диагностировать, что такое ошибка.

Я очень подозреваю, что команда at работает сама по себе, поэтому она пересматривает себя каждые 2 минуты.

Ответ 4

Если вы посмотрите на /var/spool/at/atjobs, вы найдете файлы .SEQ и файлы, похожие на это

-rwx------ 1 sergio at   5077 may  3 17:53 a000010153c71d

В этом файле есть все классы окружения, а также команда, выполняемая командой atd. Надеюсь, что это поможет

Ответ 5

Первая попытка: Как говорили другие, LaunchUpdates.php (который вы вызываете из браузера) должен запускаться сам. Вероятно, он не вызывает .../Update.php, когда вы сообщаете и намереваетесь, но ... /LaunchUpdates.php. Простую ошибку сделать и упустить, поэтому мои деньги на это. [Edit: Это не проблема.]

Вторая попытка: Теперь, когда вы добавили сгенерированный at script, мы видим, что вы действительно вызываете Update.php. Следующий ключ: подоболочка обычно увеличивает значение переменной SHLVL до значения, превышающего значение его родительского элемента. Крайне необычно, что он выходит за рамки отдельных цифр, и в этом случае он показывает, что у вас есть цепочка из более чем 700 команд, каждая из которых запускается предыдущей. Это исключает LaunchUpdates.php, как-то запускаясь через http, так как SHLVL будет reset до уровня, превышающего его значение в apache.

Мое новое предположение: Update.php выполняет как-то выполнение $SCRIPT_NAME или $SCRIPT_FILENAME, которые (как мы видим в сгенерированном script) устанавливаем с помощью at в LaunchUpdates.php вместо Update.php. Вы можете проверить, что проблема находится в Update.php, заменив его пустым файлом или заглушкой, которая просто записывает сообщение в файл: проблема должна исчезнуть.

Скорее всего, причиной являются настройки среды. Если вы не можете понять это, покажите нам код для Update.php (в упрощенной форме, но убедитесь, что проблема все еще присутствует), поэтому мы все можем взглянуть.

Изменить 2: Итак, вы подтвердили, что LaunchUpdates.php повторно запущен. Поскольку он не вызывает себя, он должен быть вызван Update.php.

Ответ 6

Автор статьи написал следующий код для выполнения планирования:

print `echo /usr/bin/php -q longThing.php | at now`;

Создание script показывает результаты команды планирования с использованием обратных циклов. Это может дать вам подсказку, если at дает какой-либо неожиданный вывод... возможно, добавьте 2>&1, чтобы увидеть, были ли какие-либо ошибки.

Ответ 7

Не так много ответов на вопрос "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 минуты и автоматически закроет автоматические послесловия script, обратите внимание, что для библиотеки требуется запуск PHP 5.4, и снова это всего лишь мысль как альтернатива, если установка 5.4 не является опцией просто игнорируйте это.

Ответ 8

Вы должны сделать свой script запустив сейчас + 2 минуты, создав и добавив файл журнала, например:

file_put_contents(__FILE__.'.log', date('c') . "\n", FILE_APPEND);

Затем вы можете легко проверить, что происходит. Двухминутное различие позволяет предположить, что оно снова перераспределяется.

Ответ 9

Попробуйте следующее:

exec('/home/user/batchProcess.sh >> ~/process.out 2>&1 | at now &');