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 &');

Другие вопросы по тегам