2013-03-07 131 views
62

我的腳本被服務器調用。從服務器我將收到ID_OF_MESSAGETEXT_OF_MESSAGE在發送http響應後繼續處理php

在我的腳本中,我會處理傳入的文本並使用params生成響應:ANSWER_TO_IDRESPONSE_MESSAGE

的問題是,我要送響應進來的"ID_OF_MESSAGE",但服務器作爲其交付給我給我的消息處理將設置他的信息(這意味着我可以給他針對這一ID),接收HTTP響應後200.

其中一個解決方案是將消息保存到數據庫並製作一些將每分鐘運行的cron,但我需要立即生成響應消息。

是否有一些解決方案如何發送到服務器HTTP響應200並繼續執行PHP腳本?

非常感謝你

回答

128

是的。你可以這樣做:

ignore_user_abort(true); 
set_time_limit(0); 

ob_start(); 
// do initial processing here 
echo $response; // send the response 
header('Connection: close'); 
header('Content-Length: '.ob_get_length()); 
ob_end_flush(); 
ob_flush(); 
flush(); 

// now the request is sent to the browser, but the script is still running 
// so, you can continue... 
+1

是否有可能通過保持連接來執行此操作? – Congelli501 2014-07-09 19:14:33

+2

優秀!這是對這個問題的唯一回應,實際上起作用! 10p + – 2014-08-01 19:13:33

+2

真棒回答!我改變的唯一的東西是'set_time_limit(0);'。您可能希望它運行時間超過默認的30秒,但如果進入無限循環,無限期地可能會導致問題!我在'php.ini'文件中設置了更長的值。 – 2014-08-21 14:25:46

19

我已經看到了很多的使用ignore_user_abort(true);暗示放在這裏的響應但是這個代碼是沒有必要的。所有這些都是確保腳本在用戶中止(通過關閉瀏覽器或按下escape來停止請求)中止之前發送響應之前繼續執行。但這不是你要問的。您要求在發送回覆後繼續執行。所有你需要的是這樣的:

// Buffer all upcoming output... 
    ob_start(); 

    // Send your response. 
    echo "Here be response"; 

    // Get the size of the output. 
    $size = ob_get_length(); 

    // Disable compression (in case content length is compressed). 
    header("Content-Encoding: none"); 

    // Set the content length of the response. 
    header("Content-Length: {$size}"); 

    // Close the connection. 
    header("Connection: close"); 

    // Flush all output. 
    ob_end_flush(); 
    ob_flush(); 
    flush(); 

    // Close current session (if it exists). 
    if(session_id()) session_write_close(); 

    // Start your background work here. 
    ... 

如果你擔心自己的工作背景會比PHP的默認腳本的執行期限較長的,然後在頂部粘set_time_limit(0);

+1

不錯的代碼!很有用! – 2016-02-12 10:13:24

+2

嘗試了很多不同的組合,這是一個工作!謝謝Kosta Kontos !!! – 2016-08-05 10:43:27

+0

對於我而言,在nginx,php 5.6中不起作用 – Dave 2016-08-10 22:38:15

0

我爲此使用php函數register_shutdown_function。

void register_shutdown_function (callable $callback [, mixed $parameter [, mixed $... ]]) 

http://php.net/manual/en/function.register-shutdown-function.php

編輯:以上不工作。看來我被一些舊文件誤導了。自PHP 4.1以來,register_shutdown_function的行爲已經發生變化linklink

+0

這不是要求的 - 這個函數基本上只是擴展了腳本終止事件,並且仍然是輸出緩衝區的一部分。 – fisk 2016-01-03 14:48:46

+0

@Fiskie你是對的。我添加了一個編輯。 – martti 2016-01-08 11:15:33

+0

發現它值得讚賞,因爲它顯示什麼是行不通的。 – Trendfischer 2016-09-09 13:06:34

3

由@vcampitelli稍微修改了答案。不要以爲你需要close標題。我在Chrome中看到重複關閉標題。

<?php 

ignore_user_abort(true); 

ob_start(); 
echo '{}'; 
header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted"); 
header("Status: 202 Accepted"); 
header("Content-Type: application/json"); 
header('Content-Length: '.ob_get_length()); 
ob_end_flush(); 
ob_flush(); 
flush(); 

sleep(10); 
+1

我在原答案中提到過這個,但我也會在這裏說。您不一定需要關閉連接,但是會發生什麼情況是,將在同一連接上請求的下一個資產將被迫等待。因此,您可以快速提供HTML,但之後您的JS或CSS文件之一的加載速度可能會很慢,因爲連接必須在獲得PHP的響應之後才能獲得下一個資源。所以出於這個原因,關閉連接是一個好主意,所以瀏覽器不必等待它被釋放。 – 2015-09-17 03:42:33

0

還有另外一種方法及其值得考慮的是,如果你不想篡改響應頭。如果你在另一個進程上啓動一個線程,被調用的函數將不會等待它的響應,並且會以最終的http代碼返回到瀏覽器。您將需要配置pthread

class continue_processing_thread extends Thread 
{ 
    public function __construct($param1) 
    { 
     $this->param1 = $param1 
    } 

    public function run() 
    { 
     //Do your long running process here 
    } 
} 

//This is your function called via an HTTP GET/POST etc 
function rest_endpoint() 
{ 
    //do whatever stuff needed by the response. 

    //Create and start your thread. 
    //rest_endpoint wont wait for this to complete. 
    $continue_processing = new continue_processing_thread($some_value); 
    $continue_processing->start(); 

    echo json_encode($response) 
} 

一旦我們執行$ continue_processing-> start()方法 PHP不會等待這個線程的返回結果,因此只要rest_endpoint考慮。完成了。

一些鏈接,以幫助並行線程

好運。

12

如果你使用FastCGI處理或PHP-FPM,您可以:

session_write_close(); //close the session 
fastcgi_finish_request(); //this returns 200 to the user, and processing continues 

// do desired processing ... 
$expensiveCalulation = 1+1; 
error_log($expensiveCalculation); 

來源:http://fi2.php.net/manual/en/function.fastcgi-finish-request.php

+0

感謝這個,花了幾個小時後,這工作在我nginx – Ehsan 2017-02-14 08:33:41

+1

fastcgi_finish_request();在iis上工作,謝謝 – dian 2017-05-03 04:22:49

0

在PHP的file_get_contents的情況下使用,連接關閉是不夠的。 php仍然等待服務器發送eof女巫。

我的解決辦法是閱讀 '內容長度:'

這裏是示例:

response.php:

<?php 

ignore_user_abort(true); 
set_time_limit(500); 

ob_start(); 
echo 'ok'."\n"; 
header('Connection: close'); 
header('Content-Length: '.ob_get_length()); 
ob_end_flush(); 
ob_flush(); 
flush(); 
sleep(30); 

注意 「\ n」 迴應收線,如果不是fget在等待eof時讀取。

read.php:

<?php 
$vars = array(
    'hello' => 'world' 
); 
$content = http_build_query($vars); 

fwrite($fp, "POST /response.php HTTP/1.1\r\n"); 
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n"); 
fwrite($fp, "Content-Length: " . strlen($content) . "\r\n"); 
fwrite($fp, "Connection: close\r\n"); 
fwrite($fp, "\r\n"); 

fwrite($fp, $content); 

$iSize = null; 
$bHeaderEnd = false; 
$sResponse = ''; 
do { 
    $sTmp = fgets($fp, 1024); 
    $iPos = strpos($sTmp, 'Content-Length: '); 
    if ($iPos !== false) { 
     $iSize = (int) substr($sTmp, strlen('Content-Length: ')); 
    } 
    if ($bHeaderEnd) { 
     $sResponse.= $sTmp; 
    } 
    if (strlen(trim($sTmp)) == 0) { 
     $bHeaderEnd = true; 
    } 
} while (!feof($fp) && (is_null($iSize) || !is_null($iSize) && strlen($sResponse) < $iSize)); 
$result = trim($sResponse); 

正如你可以看到這個腳本dosent等待約EOF如果內容長度達到。

希望這將有助於

3

我花在這個問題了幾個小時,我來到這個功能,在Apache和Nginx的工作原理:

/** 
* respondOK. 
*/ 
protected function respondOK() 
{ 
    // check if fastcgi_finish_request is callable 
    if (is_callable('fastcgi_finish_request')) { 
     /* 
     * This works in Nginx but the next approach not 
     */ 
     session_write_close(); 
     fastcgi_finish_request(); 

     return; 
    } 

    ignore_user_abort(true); 

    ob_start(); 
    $serverProtocole = filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING); 
    header($serverProtocole.' 200 OK'); 
    header('Content-Encoding: none'); 
    header('Content-Length: '.ob_get_length()); 
    header('Connection: close'); 

    ob_end_flush(); 
    ob_flush(); 
    flush(); 
} 

您可以將長處理之前調用這個函數。

1

我問這個問題拉斯姆斯·勒多夫2012年4月,理由是這些文章:

我提出一個新的PHP內置的發展在函數中通知平臺沒有進一步的輸出(在標準輸出?)將被生成(這樣的功能可能會負責關閉連接)。 Rasmus Lerdorf迴應:

請參閱Gearman。你真的不希望你的前端Web服務器做這樣的後端處理。

我可以看到他的觀點,並支持他對一些應用程序/加載場景的看法!但是,在其他一些情況下,vcampitelli等人的解決方案是很好的解決方案。

相關問題