您好,登錄后才能下訂單哦!
本篇文章為大家展示了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); } } }
上述內容就是php中多進程的應用場景有哪些,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。