濮阳杆衣贸易有限公司

主頁 > 知識庫 > php多進程應(yīng)用場景實例詳解

php多進程應(yīng)用場景實例詳解

熱門標(biāo)簽:長春人工外呼系統(tǒng)服務(wù)商 廣州防封卡外呼系統(tǒng)多少錢一個月 怎么向銷售公司推銷外呼系統(tǒng) 廣東地市地圖標(biāo)注 哪里辦理400電話 高德地圖標(biāo)注家 江西手機自動外呼防封系統(tǒng)是什么 外呼系統(tǒng)撥打暫時無法接通 仁和怎么申請400開頭的電話

本文實例講述了php多進程應(yīng)用場景。分享給大家供大家參考,具體如下:

pcntl介紹

擴展介紹

php多進程模塊依賴pcntl擴展,官方手冊介紹:http://php.net/manual/zh/book.pcntl.php

Note:

1. 此擴展在 Windows 平臺上不可用。
2. 進程控制不能被應(yīng)用在Web服務(wù)器環(huán)境,當(dāng)其被用于Web服務(wù)環(huán)境時可能會帶來意外的結(jié)果。因此,不能再PHP Web開發(fā)中使用多進程。

安裝擴展

# 通過pecl安裝pcntl擴展
sudo pecl install pcntl
# 增加 extension=pcntl.so
sodo vim /etc/php.ini
# 檢查擴展是否安裝成功
php -m | grep pcntl

處理文件

當(dāng)一個文件包含許多任務(wù)(每個任務(wù)一行),并且各任務(wù)之間不存在執(zhí)行的先后順序關(guān)系,可以將文件進行分割(分割后的文件數(shù)量與進程數(shù)一致),然后使用多進程進行處理。

例如,現(xiàn)在有10個郵箱賬號存儲在文件mailist.txt中,每次發(fā)送郵件需要耗時2s,則采用單進程依次發(fā)送完這些郵件需要耗時20。
如果采用多進程,例如3個進程進行處理,首先需要將文件按行數(shù)拆分成3個小文件,其中兩個文件是4條記錄,一個文件是2條記錄。每個進程處理一個小文件,則不同進程發(fā)送完郵件的耗時為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

相關(guān)腳本

多進程調(diào)用腳本 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);
  }
}

多進程執(zhí)行腳本 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;
}

輸出結(jié)果

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中創(chuàng)建了3個進程(a、b、c),其中a和b處理的文件中有4條記錄,c處理的文件中有2條記錄。

通過輸出結(jié)果可以得到:

1. a、b、c 三個進程同時開始執(zhí)行,開始時間戳1504499765
2. c最先完成,完成時間戳1504499769,耗時4s
3. a和c同時完成,完成時間戳1504499773,耗時8s

處理消息隊列

這是另外一種常見的常見,我們將耗時操作放入消息隊列,通過腳本從消息隊列中取出并執(zhí)行記錄。如果通過單個進程依次讀取并處理消息,容易使隊列中積累大量數(shù)據(jù),導(dǎo)致操作的延遲時間較長,這種場景可以通過多個進程來讀取并處理消息。redis中的pop操作具有原子性,不會存在多個讀取到相同的隊列消息的情況。

多進程調(diào)用腳本 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);
  }
}

多進程執(zhí)行腳本 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;

輸出結(jié)果

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

通過輸出結(jié)果可以得到

1. a、b、c三個進程同時開始執(zhí)行,時間戳為1504499844
2. a和c同時結(jié)束執(zhí)行,分別處理了3條記錄,時間戳為1504499850,耗時6s
3. b最后結(jié)束執(zhí)行,處理了4條記錄,時間戳為1504499852,耗時8s

master-worker模式

我們模擬Web服務(wù)器處理http請求的操作,對于每個請求創(chuàng)建一個進程,用于處理請求內(nèi)容。

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);

輸出結(jié)果:

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。每個請求創(chuàng)建一個進程的處理方式,總耗時是最耗時的請求操作6s。

多進程最好在方法、函數(shù)或者文件中單獨使用,這樣邏輯更加清晰,也便于分析和維護。

附錄

郵件操作類:

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);
    }
  }
}

更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《PHP進程與線程操作技巧總結(jié)》、《PHP網(wǎng)絡(luò)編程技巧總結(jié)》、《PHP基本語法入門教程》、《PHP數(shù)組(Array)操作技巧大全》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》

希望本文所述對大家PHP程序設(shè)計有所幫助。

您可能感興趣的文章:
  • PHP實現(xiàn)的多進程控制demo示例
  • 深入探究PHP的多進程編程方法
  • PHP多進程之pcntl_fork的實例詳解
  • PHP使用pcntl_fork實現(xiàn)多進程下載圖片的方法
  • PHP的pcntl多進程用法實例
  • php中實現(xiàn)進程鎖與多進程的方法
  • Linux下實現(xiàn)PHP多進程的方法分享
  • PHP多進程編程總結(jié)(推薦)
  • 分享PHP-pcntl 實現(xiàn)多進程代碼
  • php多進程模擬并發(fā)事務(wù)產(chǎn)生的問題小結(jié)
  • PHP多進程通信-消息隊列使用

標(biāo)簽:文山 黔東 海北 湘西 惠州 梅河口 廈門 濮陽

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《php多進程應(yīng)用場景實例詳解》,本文關(guān)鍵詞  php,多,進程,應(yīng)用,場景,實例,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《php多進程應(yīng)用場景實例詳解》相關(guān)的同類信息!
  • 本頁收集關(guān)于php多進程應(yīng)用場景實例詳解的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    牙克石市| 瑞金市| 吕梁市| 谷城县| 忻城县| 阿坝县| 太白县| 茌平县| 哈尔滨市| 潜江市| 天祝| 襄樊市| 嫩江县| 同心县| 雅安市| 淮南市| 西乌珠穆沁旗| 梅河口市| 赤峰市| 济源市| 壤塘县| 高雄市| 容城县| 彩票| 辉南县| 西峡县| 台中市| 曲松县| 子洲县| 深水埗区| 二手房| 略阳县| 宜州市| 信阳市| 清远市| 甘孜| 汪清县| 海淀区| 龙游县| 丰城市| 古蔺县|