免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 8132 | 回复: 8
打印 上一主题 下一主题

[原创]PHP多进程运行利器-signfork v1.0.0 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-08-06 17:37 |只看该作者 |倒序浏览
使用PHP真正的多进程运行模式(真正利用多核CPU),适用于数据采集、邮件群发、数据源更新、tcp服务器等环节。
下载地址 http://code.google.com/p/signfork/downloads/list

运行条件:*NUX环境、 编译时需要 --enable-pcntl    posix扩展


以下是主程序文件:

  1. <?php
  2. /**
  3. * Project: Signfork: php多线程库
  4. * File:    Signfork.class.php
  5. *
  6. * @link        [url]http://code.google.com/p/signfork/[/url]
  7. * @author        lajabs <hittyo at gmail dot com> QQ:124321697
  8. * @version 1.0.0 2009/8/4
  9. */



  10. class Signfork
  11. {
  12.         /**
  13.      * 设置子进程通信文件所在目录
  14.      * @var string
  15.      */
  16.         private $tmp_path='/tmp/';

  17.         /**
  18.      * Signfork引擎主启动方法
  19.          * 1、判断$arg类型,类型为数组时将值传递给每个子进程;类型为数值型时,代表要创建的进程数.
  20.          * @param object $obj 执行对象
  21.          * @param string|array $arg 用于对象中的__fork方法所执行的参数
  22.          * 如:$arg,自动分解为:$obj->__fork($arg[0])、$obj->__fork($arg[1])...
  23.          * @return array  返回   array(子进程序列=>子进程执行结果);
  24.      */
  25.         public function run($obj,$arg=1)
  26.         {
  27.                 if(!method_exists($obj,'__fork'))
  28.                 {
  29.                         exit("Method '__fork' not found!");
  30.                 }

  31.                 if(is_array($arg))
  32.                 {
  33.                         $i=0;
  34.                         foreach($arg as $key=>$val)
  35.                         {
  36.                                 $spawns[$i]=$key;
  37.                                 $i++;
  38.                                 $this->spawn($obj,$key,$val);
  39.                         }
  40.                         $spawns['total']=$i;
  41.                 }
  42.                 elseif($spawns=intval($arg))
  43.                 {
  44.                         for($i = 0; $i < $spawns; $i++)
  45.                         {
  46.                                 $this->spawn($obj,$i);
  47.                         }
  48.                 }
  49.                 else
  50.                 {
  51.                         exit('Bad argument!');
  52.                 }
  53.                 if($i>1000) exit('Too many spawns!');

  54.                 return $this->request($spawns);
  55.         }


  56.         /**
  57.      * Signfork主进程控制方法
  58.          * 1、$tmpfile 判断子进程文件是否存在,存在则子进程执行完毕,并读取内容
  59.          * 2、$data收集子进程运行结果及数据,并用于最终返回
  60.          * 3、删除子进程文件
  61.          * 4、轮询一次0.03秒,直到所有子进程执行完毕,清理子进程资源
  62.          * @param  string|array $arg 用于对应每个子进程的ID
  63.          * @return array  返回   array([子进程序列]=>[子进程执行结果]);
  64.      */
  65.         private function request($spawns)
  66.         {
  67.                 $data=array();
  68.                 $i=is_array($spawns)?$spawns['total']:$spawns;
  69.                 for($ids = 0; $ids<$i; $ids++)
  70.                 {
  71.                         while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000);
  72.                         $tmpfile=$this->tmp_path.'sfpid_'.$cid;
  73.                         $data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
  74.                         unlink($tmpfile);
  75.                 }
  76.                 return $data;
  77.         }

  78.         /**
  79.      * Signfork子进程执行方法
  80.          * 1、pcntl_fork 生成子进程
  81.          * 2、file_put_contents 将'$obj->__fork($val)'的执行结果存入特定序列命名的文本
  82.          * 3、posix_kill杀死当前进程
  83.          * @param object $obj        待执行的对象
  84.          * @param object $i                子进程的序列ID,以便于返回对应每个子进程数据
  85.          * @param object $param 用于输入对象$obj方法'__fork'执行参数
  86.      */
  87.         private function spawn($obj,$i,$param=null)
  88.         {
  89.                 if(pcntl_fork()===0)
  90.                 {
  91.                         $cid=getmypid();
  92.                         file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param));
  93.                         posix_kill($cid, SIGTERM);
  94.                         exit;
  95.                 }
  96.         }
  97. }

  98. ?>
复制代码

[ 本帖最后由 bs 于 2009-8-26 01:45 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-08-06 17:39 |只看该作者
先做一个类似采集的多进程例子:


  1. <?php
  2. include('Signfork.class.php');

  3. class test
  4. {
  5.         function __fork($arg)
  6.         {
  7.                 return file_get_contents($arg);
  8.         }
  9. }

  10. $limit                =microtime(true);
  11. $test                =new test();
  12. $Signfork        =new Signfork();



  13. $arg=array(
  14. 'http://yahoo.com',
  15. 'http://baidu.com',
  16. 'http://google.com',
  17. 'http://qq.com',
  18. 'http://163.com',
  19. 'http://sina.com'
  20. );


  21. $Signfork->run($test,$arg);
  22. echo 'Run time:'.(microtime(true)-$limit);
  23. ?>

  24. 输出:
  25. Run time:1.3541439928133

  26. 整体运行时间在一秒左右,说明多进程发挥了重要作用。
复制代码

[ 本帖最后由 bs 于 2009-8-6 17:43 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2009-08-06 17:42 |只看该作者

关于pcntl_fork后的僵尸进程

php在pcntl_fork()后生成的子进程(通常为僵尸进程)必须由pcntl_waitpid()函数进行资源释放。

注意在pcntl_waitpid()不一定释放的就是当前运行的进程,也可以是过去生成的僵尸进程(没有释放);也可以是并发时其它访问者的僵尸进程;




PS:僵尸进程

运行
#ps fax|grep defunct

<defunct>

15993 ? Z 0:00 | \ php-cgi?

<defunct>

15791 ? Z 0:00 | \ php-cgi?

<defunct>

15792 ? Z 0:00 | \ php-cgi?

<defunct>

15098 ? Z 0:00 | \ php-cgi?

<defunct>

15921 ? Z 0:00 | \ php-cgi?

<defunct>

....

在进程后面状态为:<defunct>

论坛徽章:
0
4 [报告]
发表于 2009-08-06 19:14 |只看该作者
bs是我的偶像

论坛徽章:
0
5 [报告]
发表于 2009-08-06 20:33 |只看该作者
这是 LZ 写的?

论坛徽章:
0
6 [报告]
发表于 2009-08-06 23:34 |只看该作者
阻断模式运行:
子进程会把运行结果写入进程文件,供主进程获取。
主进程通过pcntl_waitpid得知子进程运行运行结束,收集所有子进程的执行结果并返回。

非阻断模式运行:
主进程仅负责生成子进程和传递参数,不可知且不等待子进程运行结果,运行效率相对阻断模式要高出许多。

论坛徽章:
0
7 [报告]
发表于 2009-08-07 08:49 |只看该作者

强势多线程

一定要学习下,都说PHP做采集不好,有了这个,不知道怎么样了,期待啊

论坛徽章:
0
8 [报告]
发表于 2009-08-07 16:54 |只看该作者
多进程建议在 cli/cgi 方式下运作, 如果是 fastcgi 或 apache 的 isapi module 不太清楚上面的代码是不是会出现一些其它的问题.

而且你这样的处理方式, 最终主程序还是要等所有的子进程完成才能获得返回值; 再有一点主进程可以透过捕捉处理 sigchld 而不必硬生生的 while(waitpid(-1, ... ))

在cli模式下, 可以用 proc_open 及 popen 打开新的进程, 然后主进程中用 stream_select() 来管理它们, 还能完成主进程与子进程的一些交互.

论坛徽章:
0
9 [报告]
发表于 2009-08-07 17:33 |只看该作者
原帖由 hightman 于 2009-8-7 16:54 发表
多进程建议在 cli/cgi 方式下运作, 如果是 fastcgi 或 apache 的 isapi module 不太清楚上面的代码是不是会出现一些其它的问题.

而且你这样的处理方式, 最终主程序还是要等所有的子进程完成才能获得返回值;  ...


嗯,谢谢,之前摸索得比较痛苦,出现过子进程无法即时释放的情况,压力测试一上就挂了。
在子进程并行运行时,主程序理论上只等待一个子进程运行时间,一般还是可以接受的。


目前已经在fastcgi(当前nginx+php-fpm)调试正常,cli模式下我再试试你的方法,再次感谢。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP