Как я могу запустить длинный фоновый процесс из программы Perl CGI? - PullRequest
2 голосов
/ 14 октября 2010

У меня проблема с длительным CGI и ошибкой тайм-аута:

Тайм-аут в ожидании вывода из скрипта CGI

На стороне клиента есть форма, запрограммированнаяJQuery.Пользователь вводит некоторые данные и получает сообщение о начале анализа.Пользователь не ожидает, что получит больше сообщений, кроме электронного письма со ссылкой, когда данные были проанализированы.Итак, на данный момент соединение с клиентом закрыто, верно?

На стороне сервера CGI-скрипт Perl получает данные и выполняет некоторые программы на C (используя систему Perl) для их анализа.Этот процесс может занять от нескольких секунд до нескольких часов в зависимости от вводимых данных.

Затем та же самая программа CGI анализирует результаты и отправляет электронное письмо пользователю со ссылкой на веб-страницу результатов.

Поскольку для некоторых данных CGI может работать часами, я получаю сообщение об ошибке.

Я предполагаю, что увеличение ScriptTimeout - плохая идея.Я даже не уверен, что mod_cgi установлен.

Что можно сделать, чтобы избежать этой ошибки?

Сервер: Apache2 работает в Mac OS X.

Ответы [ 4 ]

5 голосов
/ 17 июня 2013

Я отключил буферизацию STDOUT, я пытался порождать фоновые процессы с помощью system (), я пробовал сочетания fork () и exec (), я пытался вызывать фоновые оболочки оболочки с фоновыми процессами подоболочек, вы называете это, но ничего не позволило родительский процесс CGI для печати вывода до завершения фонового процесса.

Вот что в конечном итоге сработало для меня:

print "Content-type: text/plain\n\n";
print "Executing background process...\n";

# fork this process
my $pid = fork();
die "Fork failed: $!" if !defined $pid;

if ($pid == 0) {
 # do this in the child
 open STDIN, "</dev/null";
 open STDOUT, ">/dev/null";
 open STDERR, ">/dev/null";
 system('bash -c \'(sleep 10; touch ./test_file)\'&');
 exit;
}

print "The background task will be finished shortly.\n";

Хитрость заключалась в закрытии STDERR в дочернем процессе (открыв его в / dev / null). Без этого наш внутренний веб-сервер считал, что родительский процесс активен до завершения дочернего процесса. Кроме того, небуферизованная автоматическая очистка не работала с нашим веб-сервером, поэтому я не мог печатать какие-либо промежуточные сообщения о состоянии.

4 голосов
/ 14 октября 2010

CGI не должен делать эту работу сам. Вместо этого он должен просто собрать пользовательский ввод и немедленно завершить его, а затем отправить отдельную программу для выполнения работы в автономном режиме. Распространенным решением является использование рабочей очереди для хранения этих запросов от пользователей, и отдельная программа прослушивает эту очередь и выполняет работу в соответствии с запросом.

Редактировать: Как правило, всегда будет запущен демон, который слушает очередь (например, на моем $ work у меня есть рабочий демон, который использует Beanstalk :: Client и beanstalkd для своей очереди заданий), но если у вас есть задания, добавляемые нечасто, тогда задание cron является хорошей первой реализацией.

В качестве альтернативного решения вы можете раскошелиться на CGI и позвонить ребенку exec, чтобы запустить рабочую программу:

# there is work to be done; dispatch the worker script in a child process.
my $pid = fork;
exec "/path/to/worker/script.pl", $arg1, $arg2 if not $pid;

# parent CGI is still alive; return an acknowledgement to the user and return.
1 голос
/ 16 ноября 2014

Просто добавить в случае сценария оболочки cgi вместо сценария cgi perl. Выполнение отключения буферизации STDOUT, как предложено в предыдущем ответе, путем перенаправления stdout, stdin и stderr в / из / dev / null при вызове сценария, который выполняется в фоновом режиме:

#!/bin/sh

./background.sh </dev/null >/dev/null 2> /dev/null

background.sh вызывает выполнение скрипта или программы в фоновом режиме. Например.

#background.sh
# wait one minute and then log date/time in log.dat file
sleep 60; echo date >> log.dat &

В этом случае вызывающий сценарий немедленно возвращается к вызывающему веб-серверу и веб-странице, в то время как сценарий, который ждет одну минуту, ожидает завершения времени ожидания. В конце дата добавляется в файл log.dat.

0 голосов
/ 14 октября 2010

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

Вы также можете увидеть Как разветвить CGI-программу на Perl, чтобы избавиться от длительных задач?

...