遇到的情况:邀请粉丝时候如果粉丝特别多,而且每邀请一个粉丝都需要调用一次极光推送接口和一次站内信接口,如果直接按照php完整的周期,时间太长,因此需要在邀请粉丝的时候分开一个进程进行专门的邀请粉丝的处理,父进程依然执行他的工作并告知前台成功,而子进程需要大量的跑邀请粉丝的工作。
一:准备工作
linux环境安装php 的扩展pcntl,使用phpize安装,如果是docker容器化环境,那么需要修改php 的Dockerfile文件进行重新build。
二:测试
$pid = pcntl_fork(); if ($pid == -1) { throw new Exception('fork子进程失败!'); } elseif ($pid > 0) { echo '我是主进程'.$pid.'<br>'; // 保持30秒,确保能被ps查到 } else { $pids = posix_getpid();//获取自己的进程pid $data['message']='dajingyao'; $data['sub']='-1'; M('test')->add($data); posix_kill($pids, 9);//杀死进程,可是实际上执行该代码后,进程成了僵尸进程了 exit(0);//子进程要exit否则会进行递归多进程,父进程不要exit否则终止多进程 }
pcntl_fork()函数生成子进程,返回的$pid 如果$pid==-1 那么此时创建子进程失败。如果$pid不是-1,那么会出现两个$pid,两个pid 分别代表两个线程,$pid==0 这个进程为子进程,$pid为一个大于0的进程为主进程。由于需要主进程去交差,子进程跑任务,主进程需要先结束,所以不需要主进程等待结果,就做如上设计。
三:问题
由于主进程没有等待子进程完成任务,这样会导致每次请求都会生成一个子进程,请求量大的话,便会出现大量的进程。
测试一下,ps -ef | grep httpd 显示如下:
可以看到一个主线程6967以及其派生出来的四个daemon进程(这个与httpd.conf配置文件有关系)。
执行刚才写好的php代码。再次在linux中输入命令:ps -ef | grep httpd
可以看到进程守护进程13638生成了一个子进程14551子进程。[httpd] <defunct>便是僵尸进程,也可以说是进入Z状态的进程。这个就是使用posix_kill($pids, 9);生成的僵尸进程,这些子进程kill不掉,只有KILL掉他的父亲才可以消失。但是他的父亲又都是httpd为了使处理速度更快而生产成的几个守护进程,KILL掉这几个守护进程,还得需要重新生成,与此同时这些僵尸进程根据项目中不会产生大量的,只需要定期清理即可。最简单的清理方法,重启apache,他的父进程都死掉了,他们肯定也丢了。如果该功能访问量特别高的话,就需要另辟蹊径了。