php多进程应用场景实例详解
发布:smiling 来源: PHP粉丝网 添加日期:2021-12-06 16:50:49 浏览: 评论:0
本文实例讲述了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
- <?php
- /**
- * Created by PhpStorm.
- * User: zhezhao
- * Date: 2017/9/4
- * Time: 上午10:05
- */
- class MailWork
- {
- private $error;
- private $name;
- public function __construct($name)
- {
- $this->name = $name;
- }
- public function getLastError(){
- return $this->error;
- }
- public function checkEnv($argc)
- {
- if (substr(php_sapi_name(), 0, 3) !== 'cli') {
- $this->error ="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);
- }
- }
- }
Tags: php多进程
相关文章
- ·PHP多进程编程实例说明(2014-01-17)
- ·php多进程几个例子(2014-06-17)
- ·PHP的pcntl多进程用法实例(2021-05-17)
- ·深入探究PHP的多进程编程方法(2021-06-16)
- ·以实例全面讲解PHP中多进程编程的相关函数的使用(2021-06-16)
- ·php多进程模拟并发事务产生的问题小结(2021-11-02)
- ·PHP基于swoole多进程操作示例(2021-12-10)
- ·PHP 多进程与信号中断实现多任务常驻内存管理实例方法(2021-12-27)
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
- PHP新手上路(一)(7)
- 惹恼程序员的十件事(5)
- PHP邮件发送例子,已测试成功(5)
- 致初学者:PHP比ASP优秀的七个理由(4)
- PHP会被淘汰吗?(4)
- PHP新手上路(四)(4)
- 如何去学习PHP?(2)
- 简单入门级php分页代码(2)
- php中邮箱email 电话等格式的验证(2)