IT TIP

HTTP 응답을 보낸 후 계속 PHP 실행

itqueen 2020. 12. 4. 21:36
반응형

HTTP 응답을 보낸 후 계속 PHP 실행


PHP 5.2 (apache mod_php로 실행)가 클라이언트에 완전한 HTTP 응답을 보낸 다음 1 분 더 작업을 계속 실행하도록하려면 어떻게해야합니까?

긴 이야기 :

몇 가지 긴 데이터베이스 요청을 실행하고 전자 메일을 보내야하는 PHP 스크립트가 있는데 실행하는 데 45-60 초가 걸립니다. 이 스크립트는 내가 제어 할 수없는 응용 프로그램에 의해 호출됩니다. PHP 스크립트에서받은 오류 메시지 (대부분 잘못된 매개 변수 오류)를보고하려면 응용 프로그램이 필요합니다.

응용 프로그램의 시간 초과 지연이 45 초보다 짧으므로 (정확한 값을 모릅니다) 따라서 PHP 스크립트의 모든 실행을 오류로 등록합니다. 따라서 가능한 한 빨리 (이상적으로는 입력 매개 변수의 유효성이 확인되는 즉시) 완전한 HTTP 응답을 클라이언트에 보내고 데이터베이스 및 이메일 처리를 실행하려면 PHP가 필요합니다.

mod_php를 실행 중이므로 pcntl_fork사용할 수 없습니다. 처리 할 데이터를 데이터베이스에 저장하고에서 실제 프로세스를 실행하여이 문제를 해결할 수 cron있지만 더 짧은 솔루션을 찾고 있습니다.


초기 요청을 처리하는 스크립트가 처리 대기열에 항목을 생성 한 다음 즉시 반환하도록합니다. 그런 다음 대기열에서 보류중인 작업을 정기적으로 실행하는 별도의 프로세스 (크론을 통해 가능)를 만듭니다.


"특수 스크립트"도구 상자에이 스 니펫이 있었지만 잃어 버렸습니다 (당시에는 구름이 흔하지 않았 음). 그래서 나는 그것을 검색하고이 질문을 떠 올렸고, 그것이 없다는 사실에 놀랐고, 더 많이 검색하고 왔습니다. 게시하려면 여기로 돌아갑니다.

<?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');
?>

실제로 몇 군데에서 사용합니다. 그리고 그것은 완전히 의미가 있습니다. banklink는 성공적인 지불 요청을 반환하고 있으며, 그럴 때 많은 서비스를 호출하고 많은 데이터를 처리해야합니다. 때로는 10 초 이상이 걸리지 만 뱅크 링크에는 고정 된 타임 아웃 기간이 있습니다. 그래서 나는 banklink를 인정하고 그에게 탈출구를 보여주고 그가 이미 떠났을 때 내 일을합니다.


필요한 것은 이런 종류의 설정입니다.

대체 텍스트


자신이나 다른 스크립트에 "http 포크"를 사용할 수 있습니다. 나는 다음과 같은 것을 의미합니다.

// parent sript, called by user request from browser

// create socket for calling child script
$socketToChild = fsockopen("localhost", 80);

// HTTP-packet building; header first
$msgToChild = "POST /sript.php?&param=value&<more params> HTTP/1.0\n";
$msgToChild .= "Host: localhost\n";
$postData = "Any data for child as POST-query";
$msgToChild .= "Content-Length: ".strlen($postData)."\n\n";

// header done, glue with data
$msgToChild .= $postData;

// send packet no oneself www-server - new process will be created to handle our query
fwrite($socketToChild, $msgToChild);

// wait and read answer from child
$data = fread($socketToChild, $dataSize);

// close connection to child
fclose($socketToChild);
...

이제 자식 스크립트 :

// parse HTTP-query somewhere and somehow before this point

// "disable partial output" or 
// "enable buffering" to give out all at once later
ob_start();

// "say hello" to client (parent script in this case) disconnection
// before child ends - we need not care about it
ignore_user_abort(1);

// we will work forever
set_time_limit(0);

// we need to say something to parent to stop its waiting
// it could be something useful like client ID or just "OK"
...
echo $reply;

// push buffer to parent
ob_flush();

// parent gets our answer and disconnects
// but we can work "in background" :)
...

주요 아이디어는 다음과 같습니다.

  • 사용자 요청에 의해 호출 된 상위 스크립트;
  • parent calls child script (same as parent or another) on the same server (or any other server) and gives request data to them;
  • parent says ok to user and ends;
  • child works.

If you need to interact with child - you can use DB as "communication medium": parent may read child status and write commands, child may read commands and write status. If you need that for several child scripts - you should keep child id on the user side to discriminate them and send that id to parent each time you want to check status of respective child.

I've found that here - http://linuxportal.ru/forums/index.php/t/22951/


What about calling a script on the file server to execute as if it had been triggered at the command line? You can do this with PHP's exec.


You can use the PHP function register-shutdown-function that will execute something after the script has completed its dialog with the browser.

See also ignore_user_abort - but you shouldn't need this function if you use the register_shutdown_function. On the same page, set_time_limit(0) will prevent your script to time out.


Using a queue, exec or cron would be an overkill to this simple task. There is no reason not to stay within the same script. This combination worked great for me:

        ignore_user_abort(true);
        $response = "some response"; 
        header("Connection: close");
        header("Content-Length: " . mb_strlen($response));
        echo $response;
        flush(); // releasing the browser from waiting
        // continue the script with the slow processing here...

read more in: How to continue process after responding to ajax request in PHP?


You can create an http request between server and server. (not browser is needed). The secret to create a background http request is setting a very small timeout, so the response is ignored.

This is a working function that I have used for that pupose:

MAY 31 PHP asynchronous background request Another way to create an asynchronous request in PHP (simulating background mode).

 /**
  * Another way to make asyncronous (o como se escriba asincrono!) request with php
  * Con esto se puede simpular un fork en PHP.. nada que envidarle a javita ni C++
  * Esta vez usando fsockopen
  * @author PHPepe
  * @param  unknown_type $url
  * @param  unknown_type $params
  */
 function phpepe_async($url, $params = array()) {
  $post_params = array(); 
     foreach ($params as $key => &$val) {
       if (is_array($val)) $val = implode(',', $val);
         $post_params[] = $key.'='.urlencode($val);
     }
     $post_string = implode('&', $post_params);

     $parts=parse_url($url);

     $fp = fsockopen($parts['host'],
         isset($parts['port'])?$parts['port']:80,
         $errno, $errstr, 30);

     $out = "POST ".$parts['path']." HTTP/1.1\r\n";
     $out.= "Host: ".$parts['host']."\r\n";
     $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
     $out.= "Content-Length: ".strlen($post_string)."\r\n";
     $out.= "Connection: Close\r\n\r\n";
     if (isset($post_string)) $out.= $post_string;

     fwrite($fp, $out);
     fclose($fp);
 }

 // Usage:
 phpepe_async("http://192.168.1.110/pepe/feng_scripts/phprequest/fork2.php");

For more info you can take a look at http://www.phpepe.com/2011/05/php-asynchronous-background-request.html


It is possible to use cURL for that, with a very short timeout. This would be your main file:

<?php>
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "http://example.com/processor.php");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10);      //just some very short timeout
    curl_exec($ch);
    curl_close($ch);
?>

And this your processor file:

<?php
    ignore_user_abort(true);                       //very important!
    for($x = 0; $x < 10; $x++)                     //do some very time-consuming task
        sleep(10);
?>

As you can see, the upper script will timeout after a short time (10 milliseconds in this case). It is possible that CURLOPT_TIMEOUT_MS will not work like this, in this case, it would be equivalent to curl_setopt($ch, CURLOPT_TIMEOUT, 1).

So when the processor file has been accessed, it will do its tasks no matter that the user (i.e. the calling file) aborts the connection.

Of course you can also pass GET or POST parameters between the pages.


You can split these functions into three scripts. 1. Initiate process and call second one via exec or command, this is also possible to run via http call. 2. second one will run database processing and at the end will start last one 3. last one will email


Bah, I misunderstood your requirements. Looks like they're actually:

  • Script receives input from an external source you do not control
  • Script processes and validates the input, and lets the external app know if they're good or not and terminates the session.
  • Script kicks off a long-running proccess.

이 경우 예, 외부 작업 대기열 및 / 또는 cron을 사용하면 작동합니다. 입력의 유효성이 확인되면 작업 세부 정보를 대기열에 삽입하고 종료합니다. 그런 다음 다른 스크립트를 실행하고 대기열에서 작업 세부 정보를 선택하고 더 긴 프로세스를 시작할 수 있습니다. Alex Howansky는 올바른 아이디어를 가지고 있습니다.

죄송합니다. 처음에 조금 훑어 봤다는 것을 인정합니다.


사용자와 함께 프로세스를 계속하는 것보다 마지막에 새로운 비동기 요청을 생성하는 것이 좋습니다.

여기 대답을 사용하여 다른 요청을 생성 할 수 있습니다. 비동기 PHP 호출?


Apache php.ini구성 파일에서 출력 버퍼링이 비활성화되어 있는지 확인합니다.

output_buffering = off

참고 URL : https://stackoverflow.com/questions/3833013/continue-php-execution-after-sending-http-response

반응형