2010-10-14 38 views
2

我有一个长期运行的CGI和超时错误的问题:如何从Perl CGI程序运行一个长的后台进程?

Timeout waiting for output from CGI script

的客户端是jQuery的编程的形式。用户输入一些数据并收到分析已启动的消息。用户不希望在分析数据时看到除收到链接的电子邮件以外的更多消息。 因此,在这一点上,与客户端的连接已关闭,对吧?

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

然后,相同的CGI程序将解析结果并通过指向结果网页的链接向用户发送电子邮件。

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

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

我能做些什么来避免这个错误?

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

+1

可能的重复[如何分叉一个Perl CGI程序来配置长时间运行的任务?](http://stackoverflow.com/questions/952132/how-can-i-fork-a-perl-cgi -program-to-hive-off-long-running-tasks) – 2010-10-14 19:51:33

回答

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. 
+0

谢谢。我了解离线解决方案。我不知道如何实现它,也许用cron作业来检查队列?但是,是不是有可能从perl/CGI执行另一个perl程序并终止而不等待呢?或者,新的perl程序将被视为另一个CGI,因此它会产生另一个超时问题? – Flope 2010-10-14 17:07:20

+0

@Flope:我已经更新了我的答案。 – Ether 2010-10-14 17:11:52

+0

谢谢乙醚。有用。子进程完成这项工作,但我仍然收到CGI超时错误。所以,我想我必须从父母发送终止信号?儿童? – Flope 2010-10-14 23:12:05

0

在这种情况下,我调用外部程序,其唯一的工作就是叉关闭处理。第一个过程可以脱离其孩子,并在真正的工作继续进行时立即返回。

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

+0

谢谢Brian。 Ehter提出了相同的解决方案,对吧?关于守护进程,我无法使Proc :: Daemon工作。所以,我在CGI中添加了两行:使用Proc :: Daemon; PROC ::守护程序::的init();我用系统来调用子进程,对吧? – Flope 2010-10-14 23:20:47

5

我残疾标准输出缓冲,我试图产卵后台进程与系统(),我想叉组合()和exec(),我试图调用与背景子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的)。如果不这样做,我们的内部网络服务器认为父进程在子进程结束之前一直处于活动状态。另外,无缓冲的自动刷新输出不能用于我们的网络服务器,因此我无法打印任何类型的临时状态消息。

0

只需添加一个cgi shell脚本而不是cgi perl脚本。执行标准输出缓冲禁用通过从/ 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 & 

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