本文实例讲述了php多进程应用场景。分享给大家供大家参考,具体如下:
pcntl介绍
扩展介绍
php多进程模块依赖pcntl扩展,官方手册介绍:http://php.net/manual/zh/book.pcntl.php
Note:
1. 此扩展在 Windows 平台上不可用。
2. 进程控制不能被应用在Web服务器环境,当其被用于Web服务环境时可能会带来意外的结果。因此,不能再PHP Web开发中使用多进程。
安装扩展
# 通过pecl安装pcntl扩展 sudo pecl install pcntl # 增加 extension=pcntl.so sodo vim /etc/php.ini # 检查扩展是否安装成功 php -m | grep pcntl
处理文件
当一个文件包含许多任务(每个任务一行),并且各任务之间不存在执行的先后顺序关系,可以将文件进行分割(分割后的文件数量与进程数一致),然后使用多进程进行处理。
例如,现在有10个邮箱账号存储在文件mailist.txt中,每次发送邮件需要耗时2s,则采用单进程依次发送完这些邮件需要耗时20。
如果采用多进程,例如3个进程进行处理,首先需要将文件按行数拆分成3个小文件,其中两个文件是4条记录,一个文件是2条记录。每个进程处理一个小文件,则不同进程发送完邮件的耗时为8、8、6,总耗时取最大值为8s。
拆分文件
原始文件 maillist.txt
000000@163.com
111111@163.com
222222@163.com
333333@163.com
444444@163.com
555555@163.com
666666@163.com
777777@163.com
888888@163.com
999999@163.com
拆分操作
split -a 1 -l 4 maillist.txt task
拆分后的文件
taska
000000@163.com
111111@163.com
222222@163.com
333333@163.com
taskb
444444@163.com
555555@163.com
666666@163.com
777777@163.com
taskc
888888@163.com
999999@163.com
相关脚本
多进程调用脚本 text_task.php
$cmds = [ ['/Users/zhezhao/www/work/text_mail.php','a'], ['/Users/zhezhao/www/work/text_mail.php','b'], ['/Users/zhezhao/www/work/text_mail.php','c'] ]; foreach ($cmds as $cmd){ $pid = pcntl_fork(); if($pid == -1){ exit('create process failed'); } if($pid > 0){ pcntl_wait($status,WNOHANG); }else{ pcntl_exec('/usr/bin/php',$cmd); } }
多进程执行脚本 text_mail.php
require 'MailWork.php'; $name = $argv[1]; echo $name." start #".time().PHP_EOL; $worker = new MailWork($name); $res = $worker->text_mail($argc,$argv); if($res === false){ echo $worker->getLastError(); }else{ echo $name." ".$res." works done # ".time().PHP_EOL; }
输出结果
c start #1504499765
b start #1504499765
a start #1504499765
b#mailto:444444@163.com
c#mailto:888888@163.com
a#mailto:000000@163.com
b#mailto:555555@163.com
a#mailto:111111@163.com
c#mailto:999999@163.com
c 2 works done # 1504499769
a#mailto:222222@163.com
b#mailto:666666@163.com
b#mailto:777777@163.com
a#mailto:333333@163.com
b 4 works done # 1504499773
a 4 works done # 1504499773
在text_task.php中创建了3个进程(a、b、c),其中a和b处理的文件中有4条记录,c处理的文件中有2条记录。
通过输出结果可以得到:
1. a、b、c 三个进程同时开始执行,开始时间戳1504499765
2. c最先完成,完成时间戳1504499769,耗时4s
3. a和c同时完成,完成时间戳1504499773,耗时8s
处理消息队列
这是另外一种常见的常见,我们将耗时操作放入消息队列,通过脚本从消息队列中取出并执行记录。如果通过单个进程依次读取并处理消息,容易使队列中积累大量数据,导致操作的延迟时间较长,这种场景可以通过多个进程来读取并处理消息。redis中的pop操作具有原子性,不会存在多个读取到相同的队列消息的情况。
多进程调用脚本 redis_task.php
$redis = new Redis(); $redis->connect('192.168.1.10'); $task_key = 'task_list'; $task_list = [ '000000@163.com', '111111@163.com', '222222@163.com', '333333@163.com', '444444@163.com', '555555@163.com', '666666@163.com', '777777@163.com', '888888@163.com', '999999@163.com', ]; foreach ($task_list as $task){ $redis->lPush($task_key,$task); } $cmds = [ ['/Users/zhezhao/www/work/redis_mail.php','a'], ['/Users/zhezhao/www/work/redis_mail.php','b'], ['/Users/zhezhao/www/work/redis_mail.php','c'] ]; foreach ($cmds as $cmd){ $pid = pcntl_fork(); if($pid == -1){ exit('create process failed'); } if($pid > 0){ pcntl_wait($status,WNOHANG); }else{ pcntl_exec('/usr/bin/php',$cmd); } }
多进程执行脚本 redis_mail.php
require 'MailWork.php'; $name = $argv[1]; echo $name." start #".time().PHP_EOL; $worker = new MailWork($name); $redis = new Redis(); $redis->connect('192.168.1.10'); $task_key = 'task_list'; while($redis->lLen($task_key)>0){ $mailto = $redis->rPop($task_key); $worker->redis_mail($mailto); } echo $name." work done # ".time().PHP_EOL;
输出结果
b start #1504499844
c start #1504499844
a start #1504499844
b#mailto:000000@163.com
a#mailto:111111@163.com
c#mailto:222222@163.com
b#mailto:333333@163.com
a#mailto:444444@163.com
c#mailto:555555@163.com
b#mailto:666666@163.com
a#mailto:777777@163.com
c#mailto:888888@163.com
c work done # 1504499850
a work done # 1504499850
b#mailto:999999@163.com
b work done # 1504499852
通过输出结果可以得到
1. a、b、c三个进程同时开始执行,时间戳为1504499844
2. a和c同时结束执行,分别处理了3条记录,时间戳为1504499850,耗时6s
3. b最后结束执行,处理了4条记录,时间戳为1504499852,耗时8s
master-worker模式
我们模拟Web服务器处理http请求的操作,对于每个请求创建一个进程,用于处理请求内容。
class WebServer { private $list; public function __construct() { $this->list = []; } public function worker($request){ $pid = pcntl_fork(); if($pid == -1){ return false; } if($pid > 0){ return $pid; } if($pid == 0){ $time = $request[0]; $method = $request[1]; $start = time(); echo getmypid()."\t start ".$method."\tat".$start.PHP_EOL; sleep($time); $end = time(); $cost = $end-$start; echo getmypid()."\t stop \t".$method."\tat:".$end."\tcost:".$cost.PHP_EOL; exit(0); } } public function master($requests){ $start = time(); echo "All request handle stop at ".$start.PHP_EOL; foreach ($requests as $request){ $pid = $this->worker($request); if(!$pid){ echo 'handle fail!'.PHP_EOL; return; } array_push($this->list,$pid); } while(count($this->list)>0){ foreach ($this->list as $k=>$pid){ $res = pcntl_waitpid($pid,$status,WNOHANG); if($res == -1 || $res > 0){ unset($this->list[$k]); } } usleep(100); } $end = time(); $cost = $end - $start; echo "All request handle stop at ".$end."\t cost:".$cost.PHP_EOL; } } $requests = [ [1,'GET index.php'], [2,'GET index.php'], [3,'GET index.php'], [4,'GET index.php'], [5,'GET index.php'], [6,'GET index.php'] ]; $server = new WebServer(); $server->master($requests);
输出结果:
All request handle stop at 1504513048
18945 start GET index.php at1504513048
18944 start GET index.php at1504513048
18946 start GET index.php at1504513048
18947 start GET index.php at1504513048
18948 start GET index.php at1504513048
18949 start GET index.php at1504513048
18944 stop GET index.php at:1504513049 cost:1
18945 stop GET index.php at:1504513050 cost:2
18946 stop GET index.php at:1504513051 cost:3
18947 stop GET index.php at:1504513052 cost:4
18948 stop GET index.php at:1504513053 cost:5
18949 stop GET index.php at:1504513054 cost:6
All request handle stop at 1504513054 cost:6
如果依次处理请求,总耗时为1+2+3+4+5+6=21s。每个请求创建一个进程的处理方式,总耗时是最耗时的请求操作6s。
多进程最好在方法、函数或者文件中单独使用,这样逻辑更加清晰,也便于分析和维护。
附录
邮件操作类:
MailWork.php
<"This Programe can only be run in CLI mode"; return false; } if($argc!=2){ $this->error = 'wrong params'; return false; } return true; } public function getFileName($argv){ $filename = "task".$argv[1]; if(!file_exists($filename)){ $this->error = 'file does not exits'; return false; }else{ return $filename; } } public function sendMail($mailto) { sleep(2); echo $this->name."#mailto:".$mailto.PHP_EOL; } public function text_mail($argc,$argv){ if(!$this->checkEnv($argc)){ return false; } $filename = $this->getFileName($argv); if(!$filename){ return false; } $fp = fopen($filename,'r'); $counter = 0; while(!feof($fp)){ $line = fgets($fp); $mailto = substr($line,0,strlen($line)-1); if(preg_match('/^\w+@\w+\.\w+$/',$mailto)){ $this->sendMail($mailto); $counter++; } } return $counter; } public function redis_mail($mailto){ if(preg_match('/^\w+@\w+\.\w+$/',$mailto)){ $this->sendMail($mailto); } } }
更多关于PHP相关内容感兴趣的读者可查看本站专题:《PHP进程与线程操作技巧总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》、《PHP数组(Array)操作技巧大全》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》
希望本文所述对大家PHP程序设计有所帮助。
php,多进程
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 【雨果唱片】中国管弦乐《鹿回头》WAV
- APM亚流新世代《一起冒险》[FLAC/分轨][106.77MB]
- 崔健《飞狗》律冻文化[WAV+CUE][1.1G]
- 罗志祥《舞状元 (Explicit)》[320K/MP3][66.77MB]
- 尤雅.1997-幽雅精粹2CD【南方】【WAV+CUE】
- 张惠妹.2007-STAR(引进版)【EMI百代】【WAV+CUE】
- 群星.2008-LOVE情歌集VOL.8【正东】【WAV+CUE】
- 罗志祥《舞状元 (Explicit)》[FLAC/分轨][360.76MB]
- Tank《我不伟大,至少我能改变我。》[320K/MP3][160.41MB]
- Tank《我不伟大,至少我能改变我。》[FLAC/分轨][236.89MB]
- CD圣经推荐-夏韶声《谙2》SACD-ISO
- 钟镇涛-《百分百钟镇涛》首批限量版SACD-ISO
- 群星《继续微笑致敬许冠杰》[低速原抓WAV+CUE]
- 潘秀琼.2003-国语难忘金曲珍藏集【皇星全音】【WAV+CUE】
- 林东松.1997-2039玫瑰事件【宝丽金】【WAV+CUE】