[php] 연결을 일찍 종료하려면 어떻게합니까?

상당히 긴 프로세스를 시작하는 AJAX 호출 (JQuery를 통해)을 시도하고 있습니다. 스크립트가 프로세스가 시작되었음을 나타내는 응답을 단순히 보내길 원하지만 JQuery는 PHP 스크립트가 실행될 때까지 응답을 반환하지 않습니다.

“닫기”헤더 (아래)와 출력 버퍼링으로 시도했습니다. 둘 다 작동하지 않는 것 같습니다. 어떤 추측? 아니면 JQuery에서해야 할 일입니까?

<?php

echo( "We'll email you as soon as this is done." );

header( "Connection: Close" );

// do some stuff that will take a while

mail( 'dude@thatplace.com', "okay I'm done", 'Yup, all done.' );

?>



답변

다음 PHP 매뉴얼 페이지 (사용자 노트 포함)는 PHP 스크립트를 종료하지 않고 브라우저에 대한 TCP 연결을 닫는 방법에 대한 여러 지침을 제안합니다.

아마도 닫기 헤더를 보내는 것보다 약간 더 많은 것이 필요합니다.


OP는 다음을 확인합니다. 예 , 이것이 트릭을 수행했습니다. 여기에 복사 된 user-note # 71172 (2006 년 11 월)를 가리 킵니다 .

PHP 스크립트를 실행하는 동안 사용자 브라우저 연결을 닫는 것은 [PHP] 4.1 이후로 register_shutdown_function()사용자 연결을 자동으로 닫지 않도록 의 동작 이 수정 된 이후 문제가되었습니다 .

sts at mail dot xubion dot hu 원래 솔루션 게시 :

<?php
header("Connection: close");
ob_start();
phpinfo();
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
sleep(13);
error_log("do something in the background");
?>

어떤 당신이 대체 될 때까지 잘 작동 phpinfo()을 위해 echo('text I want user to see');이 경우 헤더가 전송되지 않습니다!

해결책은 헤더 정보를 보내기 전에 명시 적으로 출력 버퍼링을 끄고 버퍼를 지우는 것입니다. 예:

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true); // just to be safe
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 !
// Do processing here 
sleep(30);
echo('Text user will never see');
?>

이걸 알아 내려고 3 시간을 보냈습니다. 누군가에게 도움이되기를 바랍니다. 🙂

테스트 :

  • IE 7.5730.11
  • Mozilla Firefox 1.81

2010 년 7 월 후반에 Arctic Fire 관련 답변 에서 후속 조치 인 두 개의 추가 사용자 노트를 위의 항목에 연결했습니다.


답변

다음 2 개의 헤더를 전송해야합니다.

Connection: close
Content-Length: n (n = size of output in bytes )

출력 크기를 알아야하므로 출력을 버퍼링 한 다음 브라우저에 플러시해야합니다.

// buffer all upcoming output
ob_start();
echo "We'll email you as soon as this is done.";

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');

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

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) session_write_close();

/******** background process starts here ********/

또한 웹 서버가 출력에서 ​​자동 gzip 압축을 사용하는 경우 (즉, mod_deflate를 사용하는 Apache) 출력의 실제 크기가 변경되고 Content-Length가 더 이상 정확하지 않기 때문에 작동하지 않습니다. 특정 스크립트에서 gzip 압축을 비활성화합니다.

자세한 내용은 http://www.zulius.com/how-to/close-browser-connection-continue-execution을 참조 하십시오 .


답변

PHP-FPM과 함께 Fast-CGI를 사용하여 fastcgi_end_request()함수 를 사용할 수 있습니다 . 이러한 방식으로 응답이 이미 클라이언트에 전송 된 동안 일부 처리를 계속할 수 있습니다.

여기 PHP 매뉴얼에서 찾을 수 있습니다 : FastCGI Process Manager (FPM) ; 그러나 그 기능은 설명서에 더 이상 문서화되어 있지 않습니다. 다음은 PHP-FPM 에서 발췌 한 것입니다 . PHP FastCGI Process Manager Wiki :


fastcgi_finish_request ()

범위 : PHP 함수

카테고리 : 최적화

이 기능을 사용하면 일부 PHP 쿼리의 구현 속도를 높일 수 있습니다. 스크립트 실행 과정에서 서버 응답에 영향을주지 않는 동작이있을 때 가속이 가능합니다. 예를 들어 페이지가 형성되고 웹 서버에 전달 된 후에 memcached에 세션을 저장할 수 있습니다.fastcgi_finish_request()응답 출력을 중지하는 PHP 기능입니다. 웹 서버는 즉시 “느리고 슬프게”응답을 클라이언트로 전송하기 시작하며 동시에 PHP는 세션 저장, 다운로드 한 비디오 변환, 모든 종류의 처리와 같은 쿼리 컨텍스트에서 많은 유용한 작업을 수행 할 수 있습니다. 통계 등

fastcgi_finish_request() 종료 기능 실행을 호출 할 수 있습니다.


참고 : fastcgi_finish_request()특질 호출하는 flush, print또는 echo초기 스크립트를 종료됩니다.

이러한 문제를 방지하려면 통화 ignore_user_abort(true)직전 또는 직후에 fastcgi_finish_request전화 할 수 있습니다 .

ignore_user_abort(true);
fastcgi_finish_request();


답변

완전한 버전 :

ignore_user_abort(true);//avoid apache to kill the php running
ob_start();//start buffer output

echo "show something to user";
session_write_close();//close session file on server side to avoid blocking other requests

header("Content-Encoding: none");//send header to avoid the browser side to take content as gzip format
header("Content-Length: ".ob_get_length());//send length header
header("Connection: close");//or redirect to some url: header('Location: http://www.google.com');
ob_end_flush();flush();//really send content, can't change the order:1.ob buffer to normal buffer, 2.normal buffer to output

//continue do something on server side
ob_start();
sleep(5);//the user won't wait for the 5 seconds
echo 'for diyism';//user can't see this
file_put_contents('/tmp/process.log', ob_get_contents());
ob_end_clean();


답변

더 나은 해결책은 백그라운드 프로세스를 포크하는 것입니다. 유닉스 / 리눅스에서는 매우 간단합니다.

<?php
echo "We'll email you as soon as this is done.";
system("php somestuff.php dude@thatplace.com >/dev/null &");
?>

더 나은 예를 보려면이 질문을 확인해야합니다.

PHP는 백그라운드 프로세스를 실행합니다.


답변

Linux 서버와 루트 액세스 권한이 있다고 가정하면 이것을 시도하십시오. 내가 찾은 가장 간단한 해결책입니다.

다음 파일에 대한 새 디렉터리를 만들고 전체 권한을 부여하십시오. (나중에 더 안전하게 만들 수 있습니다.)

mkdir test
chmod -R 777 test
cd test

이 파일을 bgping.

echo starting bgping
ping -c 15 www.google.com > dump.txt &
echo ending bgping

를 참고 &. ping 명령은 현재 프로세스가 echo 명령으로 이동하는 동안 백그라운드에서 실행됩니다. www.google.com을 15 번 핑하며 약 15 초가 걸립니다.

실행 가능하게 만드십시오.

chmod 777 bgping

이 파일을 bgtest.php.

<?php

echo "start bgtest.php\n";
exec('./bgping', $output, $result)."\n";
echo "output:".print_r($output,true)."\n";
echo "result:".print_r($result,true)."\n";
echo "end bgtest.php\n";

?>

브라우저에서 bgtest.php를 요청하면 ping 명령이 완료 될 때까지 약 15 초 동안 기다리지 않고 다음 응답을 빠르게 받아야합니다.

start bgtest.php
output:Array
(
    [0] => starting bgping
    [1] => ending bgping
)

result:0
end bgtest.php

이제 ping 명령이 서버에서 실행 중이어야합니다. ping 명령 대신 PHP 스크립트를 실행할 수 있습니다.

php -n -f largejob.php > dump.txt &

도움이 되었기를 바랍니다!


답변

다음은 gzip 압축과 함께 작동하는 Timbo의 코드 수정입니다.

// buffer all upcoming output
if(!ob_start("ob_gzhandler")){
    define('NO_GZ_BUFFER', true);
    ob_start();
}
echo "We'll email you as soon as this is done.";

//Flush here before getting content length if ob_gzhandler was used.
if(!defined('NO_GZ_BUFFER')){
    ob_end_flush();
}

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');

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

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) session_write_close();

/******** background process starts here ********/