PHP是世界上最好的语言,但是总被“同行们”吐槽不支持异步。其实我们要实现异步也非常简单,之前看到鸟哥的一篇写PHP异步执行的博文 PHP实现异步调用方法研究,这篇文章还是08年的,到今天PHP发展快10年了,对于异步调用也有了更多新的玩法。
当然还有很多新的特性,这里不再细说,总之PHP越是被黑越是能快速发展。
我们都知道PHP是支持多进程编程的,那完全可以新建一个进程去实现异步的调用。比如调用popen()方法,但是管道的方式传参异常麻烦,不过多进程这个方法是绝对可行的。如果要实现多进程的功能,毫无疑问我们会选择PHP官方提供的 pcntl 扩展,PHP默认会安装pcntl扩展,如果代码运行提示找不到pcntl扩展,可自行到php-src下载,选择好版本通过phpize安装即可。代码如下
<?php /** * User: layne.xfl * Date: 2017/5/12 * Time: 下午01:24 */ class Arrow{ static $instance; /** * @return static */ public static function getInstance(){ if (null == Arrow::$instance) Arrow::$instance = new Arrow(); return Arrow::$instance; } public function run($rb){ $pid = pcntl_fork(); if($pid > 0){ pcntl_wait($status); }elseif($pid == 0){ $cid = pcntl_fork(); if($cid > 0){ //这里放空 }elseif($cid == 0){ $rb(); }else{ exit(); } }else { exit(); } } } //离弦之箭---调用方法 $time_out = 30; Arrow::getInstance()->run(function() use ($time_out){ //这里写我们要执行的代码 sleep($time_out); });我给这个功能取了一个很生动的名字--离弦之箭。代表异步调用,我们的弓箭射出去后并不关心它的结果因为发送这个动作做了就行。比如打个10M的log,通知10个人(发10条短信)。
代码说明:首先Arrow类是个单例类,减少多次调用的开销。run()方法传递的是一个匿名函数,这样我们能非常方便的传递参数,并且保留上下文。
这个类最难的地方在于多进程的处理。因为我们要尽可能快的将数据返回给用户,所以主进程越快结束越好。但是我们又需要子进程来执行我们耗时的操作,执行完退出才行。如果不等子进程执行完就将父进程退出会出现什么结果呢?结果就是子进程会常驻内存变成僵死进程。那我们有什么办法让子进程执行完之后就自动结束呢?答案是很难……那么儿子进程这么不听话,孙子进程会不会听话一点呢??答案是孙子进程执行结束后会被系统进程回收并销毁(还是孙子听话)。所以我在代码中使用了如下方法:当前请求进程fork出子进程,子进程fork出孙子进程,主进程和子进程都先行退出,最后由孙子进程来执行耗时操作,最后完美的解决了僵死进程问题。
当然这个方法的缺点就是调用的时候会多产生一个php-fpm的进程。关于php-fpm进程的管理和规划又是另一个话题了。扩展阅读 PHP进程间通信
我的博客-原文链接:PHP异步的的玩法