http 응답을 보낸 후 PHP 처리 계속
내 스크립트는 서버에서 호출됩니다. 서버에서 수신 ID_OF_MESSAGE
하고 TEXT_OF_MESSAGE
.
내 스크립트에서 들어오는 텍스트를 처리하고 params : ANSWER_TO_ID
및 RESPONSE_MESSAGE
.
문제는 내가 incomming에 응답을 보내고 "ID_OF_MESSAGE"
있지만 처리 할 메시지를 보내는 서버는 http 응답 200을 수신 한 후 자신의 메시지를 나에게 전달 된 것으로 설정합니다 (즉, 해당 ID에 대한 응답을 보낼 수 있음을 의미합니다).
해결책 중 하나는 메시지를 데이터베이스에 저장하고 매분 실행될 크론을 만드는 것이지만, 즉시 응답 메시지를 생성해야합니다.
서버 http 응답 200으로 보내는 방법과 php 스크립트를 계속 실행하는 방법이 있습니까?
정말 고마워
예. 다음과 같이 할 수 있습니다.
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...
여기에서 사용을 제안하는 많은 응답을 ignore_user_abort(true);
보았지만이 코드는 필요하지 않습니다. 이 모든 작업은 사용자가 중단 한 경우 응답이 전송되기 전에 스크립트가 계속 실행되도록하는 것입니다 (브라우저를 닫거나 Esc 키를 눌러 요청을 중지). 그러나 그것은 당신이 요구하는 것이 아닙니다. 응답이 전송 된 후 실행을 계속하도록 요청합니다. 필요한 것은 다음과 같습니다.
// 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);
맨 위에 머 무르 십시오.
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);
출처 : https://www.php.net/manual/en/function.fastcgi-finish-request.php
이 문제에 대해 몇 시간을 보냈고 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();
}
긴 처리 전에이 함수를 호출 할 수 있습니다.
@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);
이를 위해 php 함수 register_shutdown_function을 사용합니다.
void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] )
http://php.net/manual/en/function.register-shutdown-function.php
편집 : 위의 내용이 작동하지 않습니다. 오래된 문서에 오해 된 것 같습니다. register_shutdown_function의 동작은 PHP 4.1 링크 링크 이후 변경되었습니다.
php file_get_contents 사용의 경우 연결 종료만으로는 충분하지 않습니다. PHP는 여전히 서버에서 eof 마녀 보내기를 기다립니다.
내 해결책은 'Content-Length :'를 읽는 것입니다.
다음은 샘플입니다.
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);
fget이 eof를 기다리는 동안 읽지 않으면 닫기 라인에 대한 응답으로 "\ n"을 참고하십시오.
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);
As you can see this script dosent wait about eof if content length is reach.
hope it will help
I asked this question to Rasmus Lerdorf in April 2012, citing these articles:
- https://www.zulius.com/how-to/close-browser-connection-continue-execution/
- close a connection early
- http://php.net/manual/en/features.connection-handling.php
I suggested the development of a new PHP built-in function to notify the platform that no further output (on stdout?) will be generated (such a function might take care of closing the connection). Rasmus Lerdorf responded:
See Gearman. You really really don't want your frontend Web servers doing backend processing like this.
I can see his point, and support his opinion for some applications/ loading scenarios! However, under some other scenarios, the solutions from vcampitelli et al, are good ones.
I have something that can compressed and send the response and let other php code to execute.
function sendResponse($response){
$contentencoding = 'none';
if(ob_get_contents()){
ob_end_clean();
if(ob_get_contents()){
ob_clean();
}
}
header('Connection: close');
header("cache-control: must-revalidate");
header('Vary: Accept-Encoding');
header('content-type: application/json; charset=utf-8');
ob_start();
if(phpversion()>='4.0.4pl1' && extension_loaded('zlib') && GZIP_ENABLED==1 && !empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== false) && (strstr($GLOBALS['useragent'],'compatible') || strstr($GLOBALS['useragent'],'Gecko'))){
$contentencoding = 'gzip';
ob_start('ob_gzhandler');
}
header('Content-Encoding: '.$contentencoding);
if (!empty($_GET['callback'])){
echo $_GET['callback'].'('.$response.')';
} else {
echo $response;
}
if($contentencoding == 'gzip') {
if(ob_get_contents()){
ob_end_flush(); // Flush the output from ob_gzhandler
}
}
header('Content-Length: '.ob_get_length());
// flush all output
if (ob_get_contents()){
ob_end_flush(); // Flush the outer ob_start()
if(ob_get_contents()){
ob_flush();
}
flush();
}
if (session_id()) session_write_close();
}
I can't install pthread and neither the previous solutions work for me. I found only the following solution to work (ref: https://stackoverflow.com/a/14469376/1315873):
<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(); // optional
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush(); // Unless both are called !
session_write_close(); // Added a line suggested in the comment
// Do processing here
sleep(30);
echo('Text user will never see');
?>
There is another approach and its worthwhile considering if you don't want to tamper with the response headers. If you start a thread on another process the called function wont wait for its response and will return to the browser with a finalized http code. You will need to configure 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)
}
Once we execute $continue_processing->start() PHP wont wait for the return result of this thread and therefore as far as rest_endpoint is considered. It is done.
Some links to help with pthreads
- How can one use multi threading in PHP applications
- http://php.net/manual/kr/pthreads.installation.php
Good luck.
I know it's an old one, but possibly usefull at this point.
With this answer i don't support the actual question but how to solve this problem correctly. Hope it helps other people to solve problems like this.
I would suggest to use RabbitMQ or similar services and run the background workload using worker instances. There is a package called amqplib for php which does all the work for you to use RabbitMQ.
Pro's:
- It's high performant
- Nicly structured and maintainable
- It's absolutly scalable with worker instances
Neg's:
- RabbitMQ must be installed on the server, this can be a problem with some web hoster.
참고URL : https://stackoverflow.com/questions/15273570/continue-processing-php-after-sending-http-response
'developer tip' 카테고리의 다른 글
MySQL FK에 대한 적절한 명명 규칙은 무엇입니까? (0) | 2020.09.01 |
---|---|
가장 가까운 0.5로 반올림하려면 어떻게합니까? (0) | 2020.08.31 |
Eclipse 시작시 SDK로드 오류 (0) | 2020.08.31 |
AdMob 용 기기 ID는 어떻게받을 수 있나요? (0) | 2020.08.31 |
Android-제목 표시 줄의 뒤로 버튼 (0) | 2020.08.31 |