1. 程式人生 > >php多程序使用場景

php多程序使用場景

pcntl介紹

擴充套件介紹

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:[email protected]
c#mailto:[email protected]
a#mailto:[email protected]
b#mailto:[email protected]
a#mailto:[email protected]
c#mailto:[email protected]
c 2 works done # 1504499769
a#mailto:[email protected]
b#mailto:[email protected]
b#mailto:[email protected]
a#mailto:[email protected]
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 = [
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
    '[email protected]',
];
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:[email protected]
a#mailto:[email protected]
c#mailto:[email protected]
b#mailto:[email protected]
a#mailto:[email protected]
c#mailto:[email protected]
b#mailto:[email protected]
a#mailto:[email protected]
c#mailto:[email protected]
c work done # 1504499850
a work done # 1504499850
b#mailto:[email protected]
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('/^\[email protected]\w+\.\w+$/',$mailto)){
                $this->sendMail($mailto);
                $counter++;
            }
        }
        return $counter;
    }
    public function redis_mail($mailto){
        if(preg_match('/^\[email protected]\w+\.\w+$/',$mailto)){
            $this->sendMail($mailto);
        }
    }
}

相關推薦

php程序使用場景

pcntl介紹 擴充套件介紹 Note: 1. 此擴充套件在 Windows 平臺上不可用。 2. 程序控制不能被應用在Web伺服器環境,當其被用於Web服務環境時可能會帶來意外的結果。因此,不能再PHP Web開發中使用多程序。

PHP程序初探 --- 利用程序開發點兒東西吧

[原文地址:https://blog.ti-node.com/blog...] 乾巴巴地叨逼叨了這麼久,時候表演真正的技術了! 做個高階點兒的玩意吧,加入我們要做一個任務系統,這個系統可以在後臺幫我們完成一大波(注意是一大波)資料的處理,那麼我們自然想到,多開幾個程序分開處理這些資料,同時我們不能執行了p

websocket基於php 記一次結合PHP程序和socket.io解決問題的經歷

記一次結合PHP多程序和socket.io解決問題的經歷     公司是做棋牌遊戲的。前段時間接到一個後臺人工鑑定並處理通牌作弊玩家的需求,其中需要根據幾個玩家的遊戲ID查詢並計算他們在某段時間內彼此之間玩牌輸贏次數和輸贏總額。   牌局資料是儲存在日誌中心的,他們把牌

PHP程序引發的msyql連線數問題

PHP多程序引發的msyql連線數問題 業務中有一塊採用了PHP的pcntl_fork多程序,希望能提高效率,但是在執行的時候資料庫報錯 PDO::prepare(): Premature end of data (mysqlnd_wireprotocol

php 程序下mysql連線 gone away

php 在命令列模式下啟動多程序如果父程序有sql查詢,可能會導致子程序裡面的sql查詢 報錯  General error: 2006 MySQL server has gone away 當fork的子程序都共用相同的mysql連線的時候,會出現該錯誤,每個子程序單獨一

php程序單例模式下的 MySQL及Redis連線錯誤修復

問題描述: 前幾天寫了個php常駐指令碼,主要邏輯如下 //跑完資料後休息60秒 $sleepTime = 60; $maxWorker = 10; while (true) { $htmlModel = new DetailHtmlMode

PHP程序抓取百度搜索結果

<?php /** * 多程序抓取百度結果頁自然結果,包括標題、摘要、圖片、連結、來源 * @since 2016-04-15 */ class NaturalResultSpider { private $_strQuery = null; pub

OSX中php程序安裝pcntl

php實現多程序,原始檔: <?php $pid = pcntl_fork(); if($pid == -1){ die('could not fork'); }else{ if ($pid) { var_dump('parent:'.$pid);

PHP程序處理並行處理任務例項

本文目的 本文通過例子講解linux環境下,使用php進行併發任務處理,以及如何通過pipe用於程序間的資料同步。寫得比較簡單,作為備忘錄。 PHP多程序 通過pcntl_XXX系列函式使用多程序功能。注意:pcntl_XXX只能執行在php CLI(命令列)環境下

從0到1優雅的實現PHP程序管理

業務場景 在我們實際的業務場景中(PHP技術棧),我們可能需要定時或者近乎實時的執行一些業務邏輯,簡單的我們可以使用unix系統自帶的crontab實現定時任務,但是對於一些實時性要求比較高的業務就不適用了,所以我們就需要一個常駐記憶體的任務管理工具,為了保證實時

php程序實現

來源:http://blog.csdn.net/e421083458/article/details/22186475 PHP多程序實現 PHP有一組程序控制函式(編譯時需要–enable-pcntl與posix擴充套件),使得php能在nginx系統中實現跟c一樣的建立

PHP程序程式設計理論+實戰

這篇文章主要介紹了PHP多程序程式設計例項,本文講解的是在Linux下實現PHP多程序程式設計,需要的朋友可以參考下: 羨慕火影忍者裡鳴人的影分身麼?沒錯,PHP程式是可以開動影分身的!想完成任務,又覺得一個程序太慢,那麼,試試用多程序來搞吧。這篇文章將會介紹一下PHP多

php 程序程式設計

第一步: $ php -m 命令檢視php是否安裝pcntl 和 posix擴充套件,若沒有則安裝 使用場景: 1. 要進行大量的網路耗時的操作 2. 要做大量的運算,並且,系統有多個cpu,為了讓使用者有更快的體驗,把一個任務,分成幾個小任務

popen——php程序利器

標題有點兒誇張, 我(們,本來想用們的,還會去掉了)執行系統命令常常用exec,system之類的, 但是今天發現了proc_open和popen,proc_open自稱比popen多一些功能,確實,proc_open有很多功能,可以與程式互動, ——但是,他是同步的,就

SimpleFork php程序併發框架

SimpleFork 基於PCNTL擴充套件的多程序程序併發框架,介面類似與Java的Thread和Runnable 為什麼要寫SimpleFork 多程序程式的編寫相比較多執行緒編寫更加複雜,需要考慮程序回收、同步、互斥、通訊等問題。對於初學者來說,處理上述問題

shell模擬php程序從redis獲取資料(個庫)

背景:現在的資料已經寫到了redis佇列裡面,完成了入棧的操作,後期打算從redis獲取資料,完成出棧的操作,出棧後然後做一系列的邏輯處理       環境: VMware虛擬機器  記憶體:1G   硬碟:60G  php環境:PHP Version 5.6.31  ph

PHP 程序實踐(一)

parent start, pid 1793 1793    2013-01-14 15:04:17 parent continue 1793    2013-01-14 15:04:18 child start, pid 1794 1794    2013-01-14 15:04:18 1794    20

執行緒程序使用場景

Python36 多執行緒、多程序的使用場景 多執行緒與多程序的使用場景 io 操作不佔用CPU(從硬碟、從網路、從記憶體讀資料都算io) 計算佔用CPU(如1+1計算) python中的執行緒是假執行緒,不同執行緒之間的切換是需要耗費資源的,因為需要儲存執行緒的上下文,不斷的切換就

PHP利用程序處理任務(一篇寫得比較容易理解的程序文章)

 PHP多程序一般應用在PHP_CLI命令列中執行php指令碼,不要在web訪問時使用。    多程序處理分解任務一般要比單程序更快。   php檢視是否安裝多程序模組: php -m

PHP簡單寫守護程序程序

public function actionSupervise($total_process = 1) { Log::info("%s begins", __METHOD__); if (!Mutex::trylo