Swoole的多進程模塊

bVbvu4m?w=600&h=360

Swoole的多進程模塊

介紹

Swoole是有自己的一個進程管理模塊,用來替代PHP的pcntl擴展,需要注意Process進程在系統是非常昂貴的資源,創建進程消耗很大,另外創建的進程過多會導致進程切換開銷大幅上升。

爲什麼不使用pcntl

  1. pcntl沒有提供進程間通信的功能
  2. pcntl不支持重定向標準輸入和輸出
  3. pcntl只提供了fork這樣原始的接口,容易使用錯誤

Swoole是怎麼解決的

  1. swoole_process提供了基於unixsock的進程間通信,使用很簡單隻需調用write/read或者push/pop即可
  2. swoole_process支持重定向標準輸入和輸出,在子進程內echo不會打印屏幕,而是寫入管道,讀鍵盤輸入可以重定向爲管道讀取數據
  3. swoole_process提供了exec接口,創建的進程可以執行其他程序,與原PHP父進程之間可以方便的通信

創建進程

函數原型:

  Swoole\Process::__construct(callable $function, $redirect_stdin_stdout = false, $create_pipe = true)
  • $function,子進程創建成功後要執行的函數,底層會自動將函數保存到對象的callback屬性上。如果希望更改執行的函數,可賦值新的函數到對象的callback屬性
  • $redirect_stdin_stdout,重定向子進程的標準輸入和輸出。啓用此選項後,在子進程內輸出內容將不是打印屏幕,而是寫入到主進程管道。讀取鍵盤輸入將變爲從管道中讀取數據。默認爲阻塞讀取。
  • $create_pipe,是否創建管道,啓用$redirect_stdin_stdout後,此選項將忽略用戶參數,強制爲true。如果子進程內沒有進程間通信,可以設置爲 false。
swoole創建多進程很簡單:new Swoole\Process('callback_function') 就可以了,比如我要同時創建6個進程,就for 循環6次就可以了。
假設前臺給後臺三組任務要求後臺去執行,每個任務大概需要執行一秒的時間,我們利用多進程的形式去實現,讓時間能夠縮短。
for ($i = 0; $i < 6; $i++) {#創建了3個子進程
    $process = new Swoole\Process(function ($process) {
        sleep(1);
        echo PHP_EOL . posix_getpid() . PHP_EOL;#獲取子進程PID
    }, false, true);
    $process->start();

進程間的通訊

如果是非常簡單的多進程執行任務,那麼進程間就不需要通訊了,實際情況下,很多業務是需要通訊的,比如,發郵件,如果自進程發送失敗了,那麼是要通知主進程的等等。

swoole_process進程間支持2種通信方式:

  1. 管道pipe
  2. 消息隊列

管道通訊

半雙工: 數據單向流動, 一端只讀, 一端只寫。
同步 vs 異步: 默認爲同步阻塞模式, 可以使用 swoole_event_add() 添加管道到 swoole 的 event loop 中, 實現異步IO

clipboard.png

管道通信是swoole_process默認的一種通信方式。當然我們也可以在實例化的時候通過參數來設定:

$process = new Swoole\Process('callback_function', false, true);

如果我們打印$process會發現,每次創建一個進程後,就會隨之創建一個管道,主進程想和哪一個進程通信,就向那個進程的管道寫入/讀取數據。

管道有2個方法,分別來寫入數據,和讀取數據。

  1. $process->write('數據');#寫入數據
  2. $process->read()#讀取數據

管道通訊方式一:

$worker = [];
for ($i = 0; $i < 3; $i++) {
    $process = new Swoole\Process(function ($process) {
        var_dump('子進程:' . $process->read());
        sleep(1);
        $process->write('子進程數據');
        echo PHP_EOL . posix_getpid() . PHP_EOL;
    }, false, true);
    $pid = $process->start();
    $worker[$pid] = $process;//把相應的進程放到同一個數組當中
    $process->write('主進程數據');
//    var_dump($process->read());//同步阻塞
}
foreach ($worker as $w) {
    var_dump('主進程:' . $w->read());
}

管道通訊方式二:

for ($i = 0; $i < 3; $i++) {
    $process = new Swoole\Process(function ($process) {
        var_dump('子進程:' . $process->read());
        sleep(1);
        $process->write('子進程數據');
        echo PHP_EOL . posix_getpid() . PHP_EOL;
    }, false, true);
    $pid = $process->start();
    $process->write('主進程數據');
    // 異步監聽管道中的數據,讀事件監聽,當管道可讀時觸發
    swoole_event_add($process->pipe, function ($pipe) use ($process) {
        var_dump('主進程:' . $process->read());
    });
//    var_dump($process->read());//同步阻塞
}

消息隊列的通訊

消息隊列:

  1. 一系列保存在內核中的消息鏈表
  2. 有一個 msgKey, 可以通過此訪問不同的消息隊列
  3. 有數據大小限制, 默認 8192
  4. 阻塞 vs 非阻塞: 阻塞模式下 pop()空消息隊列/push()滿消息隊列會阻塞, 非阻塞模式可以直接返回

swoole 中使用消息隊列:

  1. 通信模式: 默認爲爭搶模式, 無法將消息投遞給指定子進程
  2. 新建消息隊列後, 主進程就可以使用
  3. 消息隊列不可和管道一起使用, 也無法使用 swoole event loop

步驟:

  1. 啓用消息隊列作爲進程間通信:

    bool swoole_process->useQueue(int $msgkey = 0, int $mode = 2);
  2. 投遞數據到消息隊列中:

        bool swoole_process->push(string $data);
  3. 從隊列中提取數據

        string swoole_process->pop(int $maxsize = 8192);

    案例:

    for ($i = 0; $i < 3; $i++) {
        $process = new Swoole\Process(function ($process) {
            var_dump('子進程:' . $process->pop());
    //        $process->push('hello 主進程');#推送到主進程
        });
        $process->useQueue(1, 2 | swoole_process::IPC_NOWAIT);//啓用消息隊列,爭搶模式,非阻塞,可能會被任意一個子進程接收到
        $pid = $process->start();
    
        $process->push('hello 子進程');#推送到子進程,不能當做管道使用
    //    echo '主進程消息:' . $process->pop() . PHP_EOL;
    }

謝謝觀賞

謝謝大家耐心觀看,希望對您有所幫助,也希望大家提供下不同的意見,找到更有效的方式來完成,共同學習,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章