PHP使用多进程解决办法处理大数据/内存泄漏

最近在写一个数据分析系统(亿级数据),由于循环从数据库读取数据库,数据处理量较大,会造成进程崩掉。后来想到了使用多进程的办法来解决,使用循环创建多个子进程的方式来分布到各个采集节点上,再着 要先修改下PHP.INItimeout时间,再修改下my.cnf的write_time时间,不废话,都改成120,充分发挥linux多任务的优势.不曾想多线程没用成反到是带来了一系列的意外收获.让之后的许多问题迎刃而解,不敢独享特一一道来.
本文所讲的东西是源自php的pcntl_fork函数.因为这个函数依赖操作系统fork的实现,所以本文所讲的东西只适用于linux/unix.ok,那么先看看这个函数的用法吧.
 $pid = pcntl_fork();
if ($pid == -1) {
         die('could not fork');
} else if ($pid) {
         // we are the parent
         pcntl_wait($status); //Protect against Zombie children
} else {
         // we are the child
}

通过pcntl_fork创建一个子进程,如果返回值是-1的话,那么说明子进程创建失败.创建成功的进程id会返回给父进程,0返回给子进程.不好理解吧,费了很常时间明白以后,笔者习惯这样写:

$pid = pcntl_fork();
if($pid == -1){
         //创建失败咱就退出呗,没啥好说的
         die('could not fork');
}
else{
        if($pid){
                //从这里开始写的代码是父进程的,因为写的是系统程序,记得退出的时候给个返回值
               exit("I am parent!");
        }
        else{
                //从这里开始写的代码都是在新的进程里执行的,同样正常退出的话,最好也给一个返回值
                exit("I am child");
        }
}

补充:上述代码如果创建子进程成功的话,那么系统就有了两个进程,一个父进程,一个子进程。这两个进程的代码是完全相同的,只是系统分配给他们的内存等资源不一样,因为他们是独立的进程;代码运行的结果是 "I an parent!" "I amchild",不要惊讶于为什么if语句里面的两个条件结果都执行了。其实是个表象,真相是父进程执行了一遍代码,由于pcntl_fork() 函数返回的是子进程的pid数,这个数是大于0的,所以执行的分支是exit("I amparent!");而子进程在执行的时候,操作系统给pcntl_fork()函数返回的是0.所以执行的分支是exit("I amchild");至于是让父进程先执行还是让子进程先执行,这个问题可以通过别的方法实现)



      这样一改好理解多了,如果你父进程希望知道子进程正常退出的话,可以加上前面的pcntl_wait.(补充:用pcntl_fork($status)暂停父进程直到他的子进程有返回值。注意:父进程的阻塞同时会阻塞子进程。但是父进程的结束不影响子进程的运行)
      函数的用法是整明白了,在实际的工作中有什么用处呢?

1.后台程序
      命令行程序好写,服务程序也好写,笔者觉得这服务程序最难写.想想原来想在windows下写个服务啥的,又是要注册服务又是要这样那样的老费劲了.现在专职管理linux想让个命令行程序在后台运行,直接在命令后面加个&就搞定了.但是这样总觉得土.有了pcntl_fork突然发现世界是那么的美好.当主进程成功创建子进程并获得子进程的id以后,自己在临死前还不忘说一句:"我已成功运行,我的id是:xxxx(子进程的id)",完了还给系统返回个0(正常退出),哈哈,死都死的那么有尊严.
      前面说的是程序常驻内存的情况,当然了这时候要注意内存的释放以及向日志文件打印信息,而不是往屏幕上(一打印信息程序就退出的哟).还有一种情况是:程序被其它脚本调用,其它脚本只关心程序是不是正常运行,如果程序要运行很长时间才能运行完的话,最好不让脚本等待.这样的话pcntl_fork又派上用场了:)
      2.延时处理.
      有的时候,我们程序退出的时候要清理自身产生的东西,比如说要删除自己(当然了linux下面是可以删除正在运行的文件的,只是举个例子),这个时候就可以另起动一个进程,然后自己结束了,把事情交给另一个进程来做.当我们写服务程序的时候,我们肯定是写日志文件记录程序的运行情况(要不谁知道程序是不是在那睡大觉的:0).程序正常退出的时候我们可以写一条日志说程序退出了,但是当程序收到linux下伟大的kill-9的时候,怎么记录自己的退出行为呢?额.....这个和php的进程信号有关好像和这个没多大关系.
      另一种情况:一个完善的程序一般都支持start,stop,restart这样的参数.start好说,stop也好说,既然start和stop都好说了,这个restart就先stop然后再start就可以了啊.额...好像又和pcntl_fork没多大关系,当你收到重启的信号总不能还是kill然后再start吧,是不是太黄太暴力了?还是温柔一点,让当前进程退出,让另外的进程再把它拉起来吧.在windows下想实现这一点似乎很困难呢,比如程序更新的时候,一般都是单写更新程序,或者程序退出的时候另起动一个批处理启动自己:)
      3.不死进程
      其实就是传说中的双进程了.早些年的时候万象网管为了达到不被恶意结束的目的用的就是这招了.虽然咱不用担心程序被人恶意停止,但是也保不齐主程序因为任务太重抗不住自己先挂了(这种情况也不是不会发生).所以......

官方的一个CODE:
Fork in foreach: 
 <?php 
foreach ($tasks as $v) 
        {if (($pid=pcntl_fork())===-1) 
            {//... 
             continue; 
            } 
         else if ($pid) 
              {pcntl_wait($status, WNOHANG); //protect against zombie children, one wait vs one child 
              } 
         else if ($pid===0) 
              {ob_start();//prevent output to main process 
               register_shutdown_function(create_function('$pars', 'ob_end_clean();posix_kill(getmypid(), SIGKILL);'), array());//to kill self before exit();, or else the resource shared with parent will be closed 
               //... 
               exit();//avoid foreach loop in child process 
              } 
        } 
?> 



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值