关于PHP实现异步操作的研究


Posted in PHP onFebruary 03, 2013

1.为啥PHP需要异步操作?

一般来说PHP适用的场合是web页面展示等耗时比较短的任务,如果对于比较花时间的操作如resize图片、大数据导入、批量发送EDM、SMS等,就很容易出现操作超时情况。你可以说我可以设置无限超时时间,等等你也要知道PHP有一个工作模式是fastcgi,PHP无限不超时,不代表fastcgi相应不超时……如果你还想说要fastcgi相应永不超时,我建议你应该跟你们的运维人员讨论去……

这个时候异步的操作就发挥他的作用了,由于是非阻塞操作,操作会即时返回,然后在后台再慢慢干活。管你超时不超时的,我就没有在当前的进程/线程下干活。看吧是不是很美好,不过其实这也是个坑……

2.PHP可以实现异步操作吗?

答案是肯定的,不过网上各种的纯PHP实现得就有点别扭了。socket模式、挂起进程模式、有的还直接fork进程。很好,各路神仙各显神通。如果运维人员看到的话,一定会×××××你们的,不把web server跑死才怪……

那还有其他更好的方法去实现这个异步操作的可能么?有,现在我们只有想怎么开外挂了。查一下PECL主流的外挂方案有一堆的××MQ(消息队列),其中有个用于任务分配的外挂进入了我们的视线Gearman(其实这家伙才是角,我就不详细介绍了,点连接看介绍)。

3.为啥选择Gearman?

别的不说,就说他的client多,支持很多语言的client,你可以使用大部分你喜欢的语言去写worker。我个人是很烦语言之争,你喜欢用神码语言写worker都随你喜欢。有数据持久化支持(就是把队列保存到数据库介质中,那故障恢复也好做),有群集支持(其实很多××MQ都有这些功能)。PECL上有扩展,也有纯PHP实现扩展。反正这个Gearman也活了很久了,杂七杂八的问题都基本上解决了。

4.基本思路

有了Gearman这外挂就简单多了。就是向gearman发送一个任务,把执行的任务发出去,然后等待worker去调用PHP cli去运行我们的php代码。

我就写了一下一个python的worker(别问我为啥用python,1.我会python,2.linux下不用装runtime),你可以自己根据思路写一个PHP的worker,不过嘛,本人是不太信得过PHP跑的worker。其他语言饭可以用java、node.js 或者其他语言实现一个worker试试。对用Golang写worker有兴趣的朋友可以找我。

phpasync_worker_py

不好意思,里面是没有注释的。一个配置文件,一个py脚本。基本的功能也就是分析一下调用的参数,然后调用PHP Cli,就是那样子而已。要让py脚本跑起来请自行安装python的gearman模块。

然后到PHP的部分先上测试代码:

<?php
require_once 'PHPAsyncClient.php';
date_default_timezone_set('Asia/Shanghai');
class AsyncTest {
    const
        LOG_FILE = '/debug.log';
    static public function run() {
        if (PHPAsyncClient::in_callback(__FILE__)) {
            self::log('php Async callback');
            PHPAsyncClient::parse();
            return;
        }
        if (PHPAsyncClient::is_main(__FILE__)) {
            self::log('main run');
            $async_call = PHPAsyncClient::getInstance();
            $async_call->AsyncCall('AsyncTest', 'callback', array(
                'content' => 'Hello World!!!',
            ), array(
                'class' => 'AsyncTest',
                'method' => 'callback',
                'params' => array(
                    'content' => 'Hello Callback!',
                ),
            ), __FILE__);
            return;
        }
    }
    static public function callback($args) {
        self::log('AsyncTest callback run');
        self::log('AsyncTest callback args:'.print_r($args, true));
    }
    static public function log($content) {
        $fullname = dirname(__FILE__).self::LOG_FILE;
        $content = date('[Y-m-d H:i:s]').$content."\n";
        file_put_contents($fullname, $content, FILE_APPEND);
    }
}
AsyncTest::run();

就3个静态方法,一个是用于调试的log方法,其他都是字面意思。这个例子是对这种调用方式有个初步印象。然后直接上PHP的所有源码:

php_async.zip

然后应该会有很多人会说,win下安装不了gearman……所以我把java版的gearman server也放上去吧。

java-gearman-service-0.6.6.zip

5.结论

经过以上配置犀牛一样大的家伙后(要装一个Gearman,还要跑个Py脚本),我们基本上就使PHP拥有了异步调用功能,当然其中还有一个状态维护神马的要自己去实现。所以发现,其实这个方案不咋样,太复杂了。还是使用一些web service的方式去做web callback会好点(问题是web callback一样会超时……),这个请留意后续。  

为防止上面的代码无法下载,三水点靠木特打包下载

原文链接:http://my.oschina.net/wakanoc/blog/101789

PHP 相关文章推荐
php中显示数组与对象的实现代码
Apr 18 PHP
深入解析php之sphinx
May 15 PHP
php fsockopen伪造post与get方法的详解
Jun 14 PHP
php数组转换js数组操作及json_encode的用法详解
Oct 26 PHP
php获取四位字母和数字的随机数的实现方法
Jan 09 PHP
PHP SPL标准库之文件操作(SplFileInfo和SplFileObject)实例
May 11 PHP
UTF-8正则表达式如何匹配汉字
Aug 03 PHP
php while循环控制的简单实例
May 30 PHP
一个简单安全的PHP验证码类、PHP验证码
Sep 24 PHP
php+ajax实现异步上传文件或图片功能
Jul 18 PHP
详解PHP使用日期时间处理器Carbon人性化显示时间
Aug 10 PHP
php判断数组是否为空的实例方法
May 10 PHP
PHP数组循环操作详细介绍 附实例代码
Feb 03 #PHP
php中将字符串转为HTML的实体引用的一个类
Feb 03 #PHP
php处理文件的小例子(解压缩,删除目录)
Feb 03 #PHP
php函数array_merge用法一例(合并同类数组)
Feb 03 #PHP
php存储过程调用实例代码
Feb 03 #PHP
php中导出数据到excel时数字变为科学计数的解决方法
Feb 03 #PHP
php中删除字符串中最先出现某个字符的实现代码
Feb 03 #PHP
You might like
ip签名探针
2006/10/09 PHP
zf框架的zend_cache缓存使用方法(zend框架)
2014/03/14 PHP
php+html5+ajax实现上传图片的方法
2016/05/14 PHP
php 读取文件夹下所有图片、文件的实例
2018/10/17 PHP
JavaScript的面向对象(二)
2006/11/09 Javascript
各种效果的jquery ui(接口)介绍
2008/09/17 Javascript
JavaScript实现简单图片滚动附源码下载
2014/06/17 Javascript
JS拖拽插件实现步骤
2015/08/03 Javascript
原生js实现jquery函数animate()动画效果的简单实例
2016/08/21 Javascript
BootStrap 实现各种样式的进度条效果
2016/12/07 Javascript
js实现延迟加载的几种方法
2017/04/24 Javascript
Bootstrap模态框插件使用详解
2017/05/11 Javascript
React Native仿美团下拉菜单的实例代码
2017/08/08 Javascript
vue引入jq插件的实例讲解
2017/09/12 Javascript
React Native 通告消息竖向轮播组件的封装
2020/08/25 Javascript
vue实现选项卡及选项卡切换效果
2018/04/24 Javascript
koa router 多文件引入的方法示例
2019/05/22 Javascript
vscode vue 文件模板的配置方法
2019/07/23 Javascript
[01:11:32]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
python结合API实现即时天气信息
2016/01/19 Python
Matplotlib 生成不同大小的subplots实例
2018/05/25 Python
python文字和unicode/ascll相互转换函数及简单加密解密实现代码
2019/08/12 Python
Pytorch加载部分预训练模型的参数实例
2019/08/18 Python
关于Keras Dense层整理
2020/05/21 Python
django rest framework 过滤时间操作
2020/07/12 Python
中国宠物用品商城:E宠商城
2016/08/27 全球购物
来自美国主售篮球鞋的零售商店:KICKSUSA
2017/11/28 全球购物
美国网上书店:Barnes & Noble
2018/08/15 全球购物
L*SPACE官网:比基尼、泳装和度假服装
2019/03/18 全球购物
入党积极分子思想汇报
2014/01/02 职场文书
安全横幅标语
2014/06/09 职场文书
启动仪式策划方案
2014/06/14 职场文书
不听老师话的万能检讨书
2014/10/04 职场文书
个人原因辞职信模板
2015/05/13 职场文书
基于Redis结合SpringBoot的秒杀案例详解
2021/10/05 Redis
一篇文章弄清楚Ajax请求的五个步骤
2022/03/17 Javascript