2010-10-14 37 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文件。