本文摘自CSDN乐知教育《草根》杂志第四期
最近在考虑项目为用户群发邮件时想到了这个方法,觉得还有用,所以记录下来(项目情况是:用户要根据实际情况给他的客户发邮件,他的客户的数量是成千上 万,根据情况选好客户,写好邮件点发送后要等着所有这些邮件发完不太现实,所以考虑后台运行),这个方法使用到的是HTTP的特性,先整理一下思路:
1.HTTP是无状态的
2.HTTP是请求-应答模式
3.HTTP是建立在TCP之上的(TCP建立在IP之上,每次请求浏览器都会使用一个随机的端口与服务器上的web端口(如80)建立socket连接,浏览器的随机端口可以通过$_SERVER查看到)
4.浏览器在请求一个web资源时(本文指PHP文件)会等待Web服务器的响应(本文中指Apache)直到响应结束
5.如果在等待的过程中用户点击了浏览器上的停止按钮,浏览器会关掉TCP连接,也就是中止当前的HTTP的请求-应答过程,根据对TCP的理解,这个中止应该是向服务器端发送了一条TCP指令
6.底层连接TCP断掉,当前未完成的响应当然也就输出不到浏览器上了
上面几条都好理解,但第4点还有细节:
web服务器的响应http头中有一个头信息:Connection 会告知浏览器连接的保持情况,一般情况下都是:
Connection:keep-alive
并且还有另外一个头说了要保持多久:Keep-Alive:300。
那如果服务器的响应中说连接已经关闭(connection: close)了会发生什么呢?浏览器会停止等待响应,(rfc 2616)
那现在如果用户请求的PHP输出HTTP头:Connection:close。并把这些头输出到浏览器,然后再继续执行后面的代码,会是什么效果呢?
反应到浏览器上就是页面请求完了,但是php并没有执行完,也就是将继续执行,这就实现了浏览器及所请求的php异步执行的效果
例子:
ob_end_clean();#清除之前的缓冲内容,这是必需的,如果之前的缓存不为空的话,里面可能有http头或者其它内容,导致后面的内容不能及时的输出
header("Connection: close");#告诉浏览器,连接关闭了,这样浏览器就不用等待服务器的响应
#可以发送200状态码,以这些请求是成功的,要不然可能浏览器会重试,特别是有代理的情况下
ob_start();#开发当前代码缓冲
//{{逻辑代码
echo "一些处理";
//逻辑代码}}
//下面输出http的一些头信息
$size=ob_get_length();
header("Content-Length: $size");
ob_end_flush();#输出当前缓冲
flush();#输出PHP缓冲
#休眠PHP,也就是当前PHP代码的执行停止,1秒钟后PHP被唤醒,
#PHP唤醒后,继续执行下面的代码,但这个时候上面代码的结果已经输出浏览器了,
#也就是浏览器从HTTP头中知道了服务端关闭了连接,浏览器将不在等待服务器的响应,
#反应给客户的就是页面不会显示处于加载状态中,换句话说用户可以关掉当前页面,或者关掉浏览器,
#PHP唤醒后继续执行下面的代码,这也就实现了PHP后台执行的效果,
#休眠的作用只是让php先把前面的输出作完,不要急于马上执行下面的代码,休息一下而已,也就是说下面的代码
#执行的时候前面的输出应该到达浏览器了
sleep(1);
echo '这里的输出用户看不到,后台运行的';
//下面代码的任何输出都不会输出给浏览器,因为http连接已经关了,
//所以下面的代码的执行属于后台运行的
set_time_limit(0);#不受时间限制
$f = fopen('1.txt','a+');
for($i=0;$i<1000;$i++){
if (fwrite($f,$i."
") === FALSE) {
echo "Cannot write to file ($filename)";
}
}
fclose($f);
其 它情况:这种做法是让PHP主动告诉浏览器结束对话,这个过程应该是很快的,PHP收到请求后马上发送http给浏览器,但有时候的情况是PHP要先做一 些事情,然后在把连接断掉的http响应返回给浏览器,但如果这个时候出现了网络或者其它一些意外情况导致了浏览器关掉了或者失去与服务器了连接 了,PHP的响应头输出到不了浏览器上,PHP还会继续执行吗?
Web サーバーはブラウザーでリクエストを作成し、このプロセスに応答します。リクエストが PHP またはその他のサーバー側言語である場合、これらのリソースへのリクエストはサーバーの設定 (loadmodule addtype など) に従って対応する言語プロセッサに転送されます。 .) などのすべての .php アクセスは PHP によって解析されて実行され、実行結果は Apache からブラウザに返されます。ただし、これらの応答がブラウザに出力できない場合は、Apache が返します。 PHP に通知すると、PHP は現在要求されているファイルの実行を終了します。
つまり、ユーザーがリクエスト中にブラウザを閉じるか停止ボタンをクリックすると、リクエストされた PHP コードは途中までしか実行されず、この時点でデータベースへの書き込みが行われている場合、データが完全には実行されない可能性があります。完全には書かれていない。ただし、PHP は、出力がある場合にのみ、クライアントが終了したという通知を受け取ります。PHP に出力がない場合、たとえブラウザーが閉じていても、PHP コードは完全に実行されます。通常、PHP には出力バッファがあり、バッファがいっぱいになったとき、またはプログラムが正常に終了したときに出力されます。
出力がある場合にのみ PHP がブラウザが終了したかどうかを知るのはなぜですか? おそらく、出力がない場合、PHP がサイレントに実行され、Apache が通知しないためです。この場合、PHP の実行を継続する必要がある場合は、PHP の接続制御関数の一部を使用して、クライアントの接続からの終了を無視できます:
ignore_user_abort この関数は、クライアントが切断された後に PHP の実行を中止するかどうかを示します。デフォルトでは、abort です
つまり、connection_status() を通じて、ブラウザが PHP を異常終了したかどうかを知ることができます:
0? Abort
2?これら 3 つの状態は重ね合わせることができます。つまり、アボート + PHP 実行タイムアウトが存在する可能性があります。問題は、接続のステータスを取得するために connection_status() をいつ呼び出すかということです。一般的なコードで呼び出すと、0 が返されます。ブラウザが一時停止され、PHP の実行も一時停止される場合、この関数は、PHP が提供するフックである register_shutdown_function をリクエストの最後に登録するために使用されます。リクエストが正常終了しても異常終了しても、PHP が実行されている限り必ずこのコールバックが呼び出されます。このメソッドの connection_status() の戻り値は、
function shutdown(){
$f = fopen('1.txt','a+');
fwrite($f,connection_status());
} で確認できます。
register_shutdown_function('shutdown');
while(1){
# 確認してユーザーがクリックして停止すると、PHP も実行されてタイムアウトになります ファイル 1.txt に書かれているのは 2 PHP 実行タイムアウトです。
#チェックアウトしないと、ユーザーが停止をクリックし、ファイル 1.txt に 1 - ユーザーが中止されました
echo ++$i."
";
}
php はバックグラウンドで完全なコードを実行します
/////
ob_start();set_time_limit(0);
ignore_user_abort(TRUE);header("Content-Type:text /html; charset=utf-8");
header("Connection: close");
ob_end_flush();
flush();
////クロール コード
....
?>