深入PHP异步执行的详解


Posted in PHP onJune 03, 2013

Web服务器执行一个PHP脚本,有时耗时很长才能返回执行结果,后面的脚本需要等待很长一段时间才能继续执行。如果想实现只简单触发耗时脚本的执行而不等待执行结果就直接执行下一步操作,可以通过fscokopen函数来实现。
PHP支持socket编程,fscokopen函数返回一个到远程主机连接的句柄,可以像使用fopen返回的句柄一样,对它进行fwrite、fgets、fread等操作。使用fsockopen连接到本地服务器,触发脚本执行,然后立即返回,不等待脚本执行完成,即可实现异步执行PHP的效果。
示例代码如下:

<?
function triggerRequest($url, $post_data = array(), $cookie = array()){
        $method = "GET";  //通过POST或者GET传递一些参数给要触发的脚本
        $url_array = parse_url($url); //获取URL信息
        $port = isset($url_array['port'])? $url_array['port'] : 80;  
        $fp = fsockopen($url_array['host'], $port, $errno, $errstr, 30);
        if (!$fp) {
                return FALSE;
        }
        $getPath = $url_array['path'] ."?". $url_array['query'];
        if(!empty($post_data)){
                $method = "POST";
        }
        $header = $method . " " . $getPath;
        $header .= " HTTP/1.1\r\n";
        $header .= "Host: ". $url_array['host'] . "\r\n "; //HTTP 1.1 Host域不能省略
        /*以下头信息域可以省略
        $header .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 \r\n";
        $header .= "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,q=0.5 \r\n";
        $header .= "Accept-Language: en-us,en;q=0.5 ";
        $header .= "Accept-Encoding: gzip,deflate\r\n";
         */
        $header .= "Connection:Close\r\n";
        if(!empty($cookie)){
                $_cookie = strval(NULL);
                foreach($cookie as $k => $v){
                        $_cookie .= $k."=".$v."; ";
                }
                $cookie_str =  "Cookie: " . base64_encode($_cookie) ." \r\n"; //传递Cookie
                $header .= $cookie_str;
        }
        if(!empty($post_data)){
                $_post = strval(NULL);
                foreach($post_data as $k => $v){
                        $_post .= $k."=".$v."&";
                }
                $post_str  = "Content-Type: application/x-www-form-urlencoded\r\n"; 
                $post_str .= "Content-Length: ". strlen($_post) ." \r\n"; //POST数据的长度
                $post_str .= $_post."\r\n\r\n "; //传递POST数据
                $header .= $post_str;
        }
        fwrite($fp, $header);
        //echo fread($fp, 1024); //服务器返回
        fclose($fp);
        return true;
}  

这样就可以通过fsockopen()函数来触发一个PHP脚本的执行,然后函数就会返回。 接着执行下一步操作了。
现在存在一个问题:当客户端断开连接后,也就是triggerRequest发送请求后,立即关闭了连接,那么可能会引起服务器端正在执行的脚本退出。
在 PHP 内部,系统维护着连接状态,其状态有三种可能的情况:
* 0 ? NORMAL(正常)
* 1 ? ABORTED(异常退出)
* 2 ? TIMEOUT(超时)
当 PHP 脚本正常地运行 NORMAL 状态时,连接为有效。当客户端中断连接时,ABORTED 状态的标记将会被打开。远程客户端连接的中断通常是由用户点击 STOP 按钮导致的。当连接时间超过 PHP 的时限(参阅 set_time_limit() 函数)时,TIMEOUT 状态的标记将被打开。

可以决定脚本是否需要在客户端中断连接时退出。有时候让脚本完整地运行会带来很多方便,即使没有远程浏览器接受脚本的输出。默认的情况是当远程客户端连接 中断时脚本将会退出。该处理过程可由 php.ini 的 ignore_user_abort 或由 Apache .conf 设置中对应的"php_value ignore_user_abort"以及 ignore_user_abort() 函数来控制。如果没有告诉 PHP 忽略用户的中断,脚本将会被中断,除非通过 register_shutdown_function() 设置了关闭触发函数。通过该关闭触发函数,当远程用户点击 STOP 按钮后,脚本再次尝试输出数据时,PHP 将会检测到连接已被中断,并调用关闭触发函数。

脚本也有可能被内置的脚本计时器中断。默认的超时限制为 30 秒。这个值可以通过设置 php.ini 的 max_execution_time 或 Apache .conf 设置中对应的"php_value max_execution_time"参数或者 set_time_limit() 函数来更改。当计数器超时的时候,脚本将会类似于以上连接中断的情况退出,先前被注册过的关闭触发函数也将在这时被执行。在该关闭触发函数中,可以通过调用 connection_status() 函数来检查超时是否导致关闭触发函数被调用。如果超时导致了关闭触发函数的调用,该函数将返回 2。

需要注意的一点是 ABORTED 和 TIMEOUT 状态可以同时有效。这在告诉 PHP 忽略用户的退出操作时是可能的。PHP 将仍然注意用户已经中断了连接但脚本仍然在运行的情况。如果到了运行的时间限制,脚本将被退出,设置过的关闭触发函数也将被执行。在这时会发现函数 connection_status() 返回 3。
所以还在要触发的脚本中指明:

<?
    ignore_user_abort(TRUE);//如果客户端断开连接,不会引起脚本abort
 set_time_limit(0);//取消脚本执行延时上限

或使用:
<?
  
register_shutdown_function(callback fuction[, parameters]);//注册脚本退出时执行的函数

PHP 相关文章推荐
用PHP查询域名状态whois的类
Nov 25 PHP
一个php Mysql类 可以参考学习熟悉下
Jun 21 PHP
Apache环境下PHP利用HTTP缓存协议原理解析及应用分析
Feb 16 PHP
PHP 调试工具Debug Tools
Apr 30 PHP
php实现的百度搜索某地天气的小偷代码
Apr 23 PHP
destoon实现资讯信息前面调用它所属分类的方法
Jul 15 PHP
php中simplexml_load_file函数用法实例
Nov 12 PHP
php数组排序usort、uksort与sort函数用法
Nov 17 PHP
php中$_POST与php://input的区别实例分析
Jan 07 PHP
PHP生成唯一订单号的方法汇总
Apr 16 PHP
使用PHP进行微信公众平台开发的示例
Aug 21 PHP
TP3.2.3框架使用CKeditor编辑器在页面中上传图片的方法分析
Dec 31 PHP
php实现自动获取生成文章主题关键词功能的深入分析
Jun 03 #PHP
基于MySQL到MongoDB简易对照表的详解
Jun 03 #PHP
PHP Error与Logging函数的深入理解
Jun 03 #PHP
作为PHP程序员应该了解MongoDB的五件事
Jun 03 #PHP
基于Discuz security.inc.php代码的深入分析
Jun 03 #PHP
基于HBase Thrift接口的一些使用问题及相关注意事项的详解
Jun 03 #PHP
基于php在各种web服务器的运行模式详解
Jun 03 #PHP
You might like
BBS(php &amp; mysql)完整版(七)
2006/10/09 PHP
mysql From_unixtime及UNIX_TIMESTAMP及DATE_FORMAT日期函数
2010/03/21 PHP
PHP similar_text 字符串的相似性比较函数
2010/05/26 PHP
php函数与传递参数实例分析
2014/11/15 PHP
PHP Try-catch 语句使用技巧
2016/02/28 PHP
js类式继承的具体实现方法
2013/12/31 Javascript
你未必知道的JavaScript和CSS交互的5种方法
2014/04/02 Javascript
10条建议帮助你创建更好的jQuery插件
2015/05/18 Javascript
【JS+CSS3】实现带预览图幻灯片效果的示例代码
2016/03/17 Javascript
jQuery实现响应鼠标事件的图片透明效果【附demo源码下载】
2016/06/16 Javascript
js获取指定字符前/后的字符串简单实例
2016/10/27 Javascript
JavaScript模板引擎Template.js使用详解
2016/12/15 Javascript
JavaScript基于replace+正则实现ES6的字符串模版功能
2017/04/25 Javascript
vue中v-model动态生成的实例详解
2017/10/27 Javascript
原生JS实现的多个彩色小球跟随鼠标移动动画效果示例
2018/02/01 Javascript
浅谈webpack组织模块的原理
2018/03/10 Javascript
Vue插件打包与发布的方法示例
2018/08/20 Javascript
TypeScript基础入门教程之三重斜线指令详解
2018/10/22 Javascript
node和vue实现商城用户地址模块
2018/12/05 Javascript
Vue中axios的封装(报错、鉴权、跳转、拦截、提示)
2019/08/20 Javascript
如何通过shell脚本自动生成vue文件详解
2019/09/10 Javascript
vue 解决无法对未定义的值,空值或基元值设置反应属性报错问题
2020/07/31 Javascript
Python实现通过文件路径获取文件hash值的方法
2017/04/29 Python
python定时利用QQ邮件发送天气预报的实例
2017/11/17 Python
python实现归并排序算法
2018/11/22 Python
Django中如何使用sass的方法步骤
2019/07/09 Python
Python树莓派学习笔记之UDP传输视频帧操作详解
2019/11/15 Python
详解Python利用configparser对配置文件进行读写操作
2020/11/03 Python
html5手机端页面可以向右滑动导致样式受影响的问题
2018/06/20 HTML / CSS
JENNIFER BEHR官网:各种耳环和发饰
2020/06/07 全球购物
利用指针变量实现队列的入队操作
2012/04/07 面试题
经典优秀个人求职自荐信格式
2013/09/25 职场文书
小学生节约用水倡议书
2014/05/15 职场文书
2015中秋节慰问信范文
2015/03/23 职场文书
小学运动会加油稿
2015/07/22 职场文书
2015年民兵整组工作总结
2015/07/24 职场文书