我遇到了长时间运行的CGI和超时错误的问题:
超时等待CGI脚本的输出
客户端是jQuery编程的表单。用户输入一些数据并接收分析已启动的消息。除了在分析数据时带有链接的电子邮件之外,用户不希望看到更多消息。 那么,此时,与客户端的连接已关闭,对吧?
在服务器端,Perl CGI脚本获取数据并执行一些C程序(使用Perl的系统)来分析它们。此过程可能需要几秒钟到几小时,具体取决于输入的数据。
然后,相同的CGI程序会解析结果并向用户发送一封电子邮件,其中包含指向结果网页的链接。
由于某些数据,CGI可能会运行数小时我收到错误消息。
我假设增加ScriptTimeout是一个坏主意。 我甚至不确定是否安装了mod_cgi。
我该怎么做才能避免此错误?
服务器:在Mac OS X中运行的Apache2。
答案 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::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.
答案 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文件中。