Запуск задания в фоновом режиме с Perl БЕЗ ожидания возврата

Отказ от ответственности

Прежде всего, я знаю, что этот вопрос (или близкие варианты) задавался тысячу раз. Я действительно потратил несколько часов, глядя в очевидные и не столь очевидные места, но может быть что-то маленькое, что мне не хватает.

Контекст

Позвольте мне более четко определить проблему: я пишу приложение для рассылки новостей, в котором хочу, чтобы фактический процесс отправки был асинхронным. Как и в случае, пользователь нажимает «отправить», немедленно запрашивает возврат, а затем проверяет прогресс на определенной странице (например, через AJAX). Он написан в вашем традиционном стеке LAMP.

В конкретном хосте, который я использую, PHP exec () и system () отключены по соображениям безопасности, но системные функции Perl (exec, system и backticks) – нет. Так что мой обходной путь решение заключалось в создании сценария «триггера» в Perl, который вызывает фактического отправителя через CLI PHP и перенаправляется на страницу прогресса.

Где я застрял

Сама строка, вызывающая отправителя, на данный момент:

system("php -q sender.php &"); 

Проблема заключается в том, что она не сразу возвращается, но ждет завершения скрипта. Я хочу, чтобы он работал в фоновом режиме и сразу же возвращался к системному вызову. Я также попробовал запустить аналогичный скрипт в моем терминале Linux, и на самом деле приглашение не отображается до тех пор, пока скрипт не завершится, хотя мой тестовый результат не запускается, указывая, что он действительно работает в фоновом режиме.

Что я уже пробовал

  • Функция exec () Perl – тот же результат системы ().
  • Изменение команды на: «php -q sender.php | at now»), надеясь, что демон «at» вернется и что сам процесс PHP не будет привязан к Perl.
  • Выполнение команды «косвенно»: «/ bin / sh -c» php -q sender.php & '»- все еще ждет завершения отправки sender.php.
  • fork () в процессе и выполнение системного вызова в дочернем элементе (надеюсь, отсоединенный процесс) – тот же результат, что и выше

Моя тестовая среда

Чтобы быть уверенным, что я ничего не теряю, я создал скрипт sleepper.php, который просто спит пять секунд до выхода. И скрипт test.cgi, похожий на это, дословно:

 #!/usr/local/bin/perl system("php sleeper.php &"); print "Content-type: text/html\n\ndone"; 

Что мне теперь делать?

По сути вам нужно «демонировать» процесс – разблокировать дочерний элемент, а затем полностью отключить его от родителя, чтобы родитель мог безопасно завершить работу, не затрагивая ребенка.

Вы можете сделать это легко с помощью модуля CPAN Proc :: Daemon :

 use Proc::Daemon; # do everything you need to do before forking the child... # make into daemon; closes all open fds Proc::Daemon::Init(); 

Иногда STDERR и STDOUT также могут блокировать систему … Для получения обоих я использую (для большинства оболочек (bash, csh и т. Д.), Которые я использую …):

 system("php sender.php > /dev/null 2>&1 &"); 

Используйте fork() а затем вызовите system в дочернем процессе.

 my $pid = fork(); if (defined $pid && $pid == 0) { # child system($command); # or exec($command) exit 0; } # parent # ... continue ... 

Другой вариант – настроить сервер ретранслятора и рабочий процесс (или процессы), которые выполняют электронную почту. Таким образом, вы контролируете, сколько электронной почты идет одновременно, и не требуется разветвление. Клиент (ваша программа) может добавить задачу на сервер ретранслятора (в фоновом режиме, не ожидая результата, если это необходимо), а задания помещаются в очередь до тех пор, пока сервер не передаст задание доступному рабочему. Есть API perl и php для gearman, так что это очень удобно.

Удалось решить проблему. Видимо, что удерживало его от возвращения, заключалось в том, что вызов отправителя таким образом не отключил выступление. Таким образом, решение просто меняло системный вызов на:

 system("php sender.php > /dev/null &"); 

Спасибо всем за помощь. Фактически, при чтении всей истории о «демонализации» процесса у меня возникла идея отключить стандартный вывод.