2012-01-17 166 views
7

我的VPS服務器出現以下問題。運行併發PHP腳本

我有一個長時間運行的PHP腳本,它將大文件發送到瀏覽器。它確實是這樣的:

<?php 
header("Content-type: application/octet-stream"); 
readfile("really-big-file.zip"); 
exit(); 
?> 

這基本上讀取服務器的文件系統中的文件,並將其發送給瀏覽器。我不能只使用直接鏈接(並讓Apache服務於該文件),因爲應用程序中存在需要應用的業務邏輯。

問題是,雖然此類下載正在運行,但該站點不響應其他請求。

+0

不是說這是問題,但是當服務大文件時,您應該始終調用'set_time_limit(0);'。它目前對你來說應該沒有什麼區別,但是如果你在某些時候將它移到* Windows平臺,可能會遇到潛在的問題。 – DaveRandom 2012-01-17 11:47:23

+1

你是如何發現這個問題的?你是通過從同一臺機器發出多個請求來測試它嗎?你在使用會話嗎? – DaveRandom 2012-01-17 11:53:48

+0

@DaveRandom當我嘗試下載多個文件時(他們被排隊等待下載),我注意到了這個問題。我正在使用會話 - 只是試過了,看起來這個限制不會影響其他會話。感謝您的想法 - 我現在進一步調查。 – 2012-01-17 12:07:07

回答

30

您遇到有關的事實,你正在使用的會話的問題。當腳本具有正在運行的會話時,它會鎖定會話文件以防止可能破壞會話數據的併發寫入。這意味着來自同一客戶端的多個請求(使用相同的會話ID)將不會同時執行,它們將排隊並且一次只能執行一個請求。

多個用戶不會遇到此問題,因爲他們將使用不同的會話ID。這並不意味着您沒有問題,因爲您可能想要在下載文件時訪問網站,或者一次下載多個文件。

該解決方案實際上非常簡單:在開始輸出文件之前調用session_write_close()。這將關閉會話文件,釋放鎖並允許執行進一步的併發請求。

+0

謝謝,那就是問題所在。 – 2012-01-17 12:37:42

+0

好點和+1 – Lion 2012-01-20 18:02:02

+0

簡直太棒了。你讓我很快樂! – Claudix 2012-10-25 09:39:57

1

服務器以何種方式不響應其他請求?它是「等待example.com ...」還是它給出了任何類型的錯誤?

我做了類似的事情,但是我提供了分塊的文件,當客戶端接受並下載一個塊時,它給文件系統一箇中斷,這比一次提供整個東西要好,這對於文件系統和整個服務器。

編輯:雖然不是這個問題的答案,asker問關於閱讀文件chunked。這是我使用的功能。爲它提供文件的完整路徑。

function readfile_chunked($file_path, $retbytes = true) 
{ 
$buffer = ''; 
$cnt = 0; 
$chunksize = 1 * (1024 * 1024); // 1 = 1MB chunk size 
$handle = fopen($file_path, 'rb'); 
if ($handle === false) { 
    return false; 
} 
while (!feof($handle)) { 
    $buffer = fread($handle, $chunksize); 
    echo $buffer; 
    ob_flush(); 
    flush(); 
    if ($retbytes) { 
     $cnt += strlen($buffer); 
    } 
} 
    $status = fclose($handle); 
    if ($retbytes && $status) { 
     return $cnt; // return num. bytes delivered like readfile() does. 
} 
    return $status; 
} 
+0

新請求正在等待下載完成。我不太確定如何在PHP中實現塊下載? – 2012-01-17 11:47:16

+0

嗨,Emil M.我添加了我用來以塊讀取文件的函數。 – 2012-01-17 13:16:02

+0

我剛剛答覆了你的答案,請讓我解釋一下。 readfile不會將文件內容讀入內存,然後將其寫入標準輸出。不,它調用內部函數'_php_stream_passthru()',這是一個類似於你的C循環,除非它不執行刷新,因爲'output_buffering' INI設置無論如何都會實現這一點。對於輸出已經壓縮的內容,'zlib.output_compression'等應該是錯誤的。你的建議只是增加了複雜性,沒有任何好處。 – TerryE 2014-07-09 15:45:20

0

我已經嘗試了不同的方法(閱讀和小塊[查看PHP文檔上readfile評論]發送文件,用梨HTTP_Download),但我總是遇到了性能問題時,文件越來越大。

有一個Apache mod 您可以在其中執行業務邏輯,然後將下載委託給Apache。下載將不會公開。我認爲,這是針對該問題的最優雅的解決方案。

更多信息:

2

您的服務器設置可能不是您應該檢查的唯一位置。

嘗試像往常一樣從瀏覽器執行請求,然後從其他客戶端執行另一個請求。

來自同一臺機器的wget或另一臺機器上的另一臺瀏覽器。

0

同樣的事情發生在我身上,我沒有使用會話。 session.auto_start設置爲0 我的示例腳本只運行「sleep(5)」,並且在開頭添加「session_write_close()」並不能解決問題。

0

檢查你的httpd.conf文件。也許你有「KeepAlive On」,這就是爲什麼你的第二個請求掛起,直到第一個請求完成。一般而言,您的PHP腳本不應允許訪問者等待很長時間。如果您需要下載一些大的內容,請在用戶無法直接控制的單獨內部請求中進行。在完成之前,向最終用戶返回一些「執行」狀態,並在完成後處理實際結果。