如何从Perl CGI程序运行长后台进程?

时间:2010-10-14 16:14:02

标签: perl timeout apache2 cgi

我遇到了长时间运行的CGI和超时错误的问题:

  

超时等待CGI脚本的输出

客户端是jQuery编程的表单。用户输入一些数据并接收分析已启动的消息。除了在分析数据时带有链接的电子邮件之外,用户不希望看到更多消息。 那么,此时,与客户端的连接已关闭,对吧?

在服务器端,Perl CGI脚本获取数据并执行一些C程序(使用Perl的系统)来分析它们。此过程可能需要几秒钟到几小时,具体取决于输入的数据。

然后,相同的CGI程序会解析结果并向用户发送一封电子邮件,其中包含指向结果网页的链接。

由于某些数据,CGI可能会运行数小时我收到错误消息。

我假设增加ScriptTimeout是一个坏主意。 我甚至不确定是否安装了mod_cgi。

我该怎么做才能避免此错误?

服务器:在Mac OS X中运行的Apache2。

4 个答案:

答案 0 :(得分:5)

我禁用了STDOUT缓冲,我尝试使用system()生成后台进程,我尝试了fork()和exec()的组合,我尝试使用后台子shell进程调用后台bash shell,你可以命名,但是什么都不允许父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)。如果不这样做,我们的内部网络服务器就认为父进程处于活动状态,直到子进程结束。此外,无缓冲的自动刷新输出不适用于我们的网络服务器,因此我无法打印任何类型的临时状态消息。

答案 1 :(得分:3)

CGI本身不应该做这项工作。它应该只是收集用户输入并立即完成,然后分派一个单独的程序来离线完成工作。一个常见的解决方案是使用工作队列来存储来自用户的这些请求,并且单独的程序将侦听此队列并按要求执行工作。

编辑:通常,会有一个守护程序一直在运行,它会监听队列(例如,在我的工作中,我有一个使用Beanstalk::Clientbeanstalkd作为其作业队列的工作守护程序) ,但如果你很少添加工作,那么一个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.

答案 2 :(得分:0)

在这些情况下,我调用一个外部程序,其唯一的工作就是分离进程。第一个过程可以与其子女分离,并在实际工作继续进行时立即返回。

您可能还会看到How can I fork a Perl CGI program to hive off long-running tasks?

答案 3 :(得分:0)

只是为了添加cgi shell脚本而不是cgi perl脚本。通过在调用后台运行的脚本时将stdout,stdin和stderr重定向到/ from / dev / null来执行STDOUT缓冲禁用,如前面的答案所示:

#!/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 &

在这种情况下,调用脚本会立即返回到调用Web服务器和Web页面,而等待一分钟的脚本将等待等待时间等待完成。最后,日期将附加到log.dat文件中。