[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 &");
?>
더 나은 예를 보려면이 질문을 확인해야합니다.
답변
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 ********/