PHP 進程的實現與管理

應用場景

一些耗時任務:php

  1. 大數據表分表後的統計信息功能
  2. 分批發送短信或郵件功能
  3. 其餘可分目標的任務功能(不少種)

因此咱們就須要一個常駐內存的任務管理工具,爲了保證明時性,一方面咱們讓它一直執行任務(適當的睡眠,保證cpu不被100%佔用),另外一方面咱們實現多進程保證併發的執行任務,固然除此以外也可按狀況使用線程、協程實現。linux

運行模式

實現PHP進程前,需瞭解常見的php的運行模式:服務器

  1. CGI通用網關接口模式
  2. FAST-CGI模式
  3. CLI命令行模式 (php xxx.php)
  4. 模塊模式(做爲服務器模塊)

而php進程則是使用CLI命令行模式運行的併發

基本實現

PHP中提供了一個擴展pcntl,能夠利用操做系統的fork調用來實現多進程。fork調用後執行的代碼將是並行的,且只能在linux下運行。socket

$ppid = posix_getpid();// 獲取當前進程PID
$pid  = pcntl_fork(); //建立進程

switch ($pid){
        // 建立進程錯誤
    case -1:
        throw new Exception('fork子進程失敗!');
        break;

        // 子進程worker
    case 0:
        $cpid = posix_getpid();
        cli_set_process_title("我是{$ppid}的子進程,個人進程id是{$cpid}.");
        sleep(30);
        exit; // 這裏exit掉,避免worker繼續執行下面的代碼而形成一些問題
        break;

        // 主進程master
    default:
        cli_set_process_title("我是父進程,個人進程id是{$ppid}.");
        pcntl_wait($status); // 掛起父進程,等待並返回子進程狀態,防止子進程成爲殭屍進程
        break;
}

在命令行php xxx.php運行後,使用ps aux | grep 進程能夠看到:函數

clipboard.png

若是沒看到,多是中文亂碼了,使用ps aux,查看工具

clipboard.png

或者使用ps –ajft查看層次顯示oop

clipboard.png

進程管理-防止進程成爲殭屍進程

建立好了進程,那麼怎麼對子進程進行管理呢?使用信號,對子進程的管理,通常有兩種狀況:
posix_kill():此函數並不能顧名思義,它經過向子進程發送一個信號來操做子進程,在須要要時能夠選擇給子進程發送進程終止信號來終止子進程;
pcntl_waitpid():等待或返回fork的子進程狀態,若是指定的子進程在此函數調用時已經退出(俗稱殭屍進程),此函數將馬上返回,並釋放子進程的全部系統資源,此進程能夠避免子進程變成殭屍進程,形成系統資源浪費;性能

孤兒進程:父進程掛了,子進程被pid=1的init進程接管(wait/waitpid),直到子進程自身生命週期結束被系統回收資源和父進程 採起相關的回收操做
殭屍進程:子進程exit退出,父進程沒有經過wait/waitpid獲取子進程狀態,子進程佔用的進程號等描述資源符還存在,產生危害:例如進程號是有限的,沒法釋放進程號致使將來可能無進程號可用大數據

**父進程中使用:pcntl_wait或者pcntl_waitpid的目的就是防止worker成爲殭屍進程
做用:使用pcntl_wait()後,在子進程死掉後,父進程也會被中止**

最後咱們經過下圖(1-1)來簡單的總結和描述這個多進程實現的過程:

clipboard.png

進程管理-進程間通訊

隊列:如Redis,推薦
socket:推薦
管道:實現複雜,且管道(pipe),使用文件形式存在,存在硬盤IO性能瓶頸
信號:承載信息量少,很差管理

進程管理-切換爲守護進程

使用&實現
php deadloop.php &

實際多進程的使用

一個耗時10S的任務,執行2次,總耗時20S,而開2個進程,只需10S,以下:

job.php:

clipboard.png

index.php(進程開啓腳本):
echo '開始時間:'.date('H:i:s', time())."\n";

$cmds = [
    ['./job.php', 0, 50000],//執行腳本,並傳參
    ['./job.php', 50000, 100000]
];
for ($i = 0; $i < 2; $i++){
    $ppid = posix_getpid();// 獲取當前進程PID
    $pid  = pcntl_fork(); //建立進程
    switch ($pid){
        // 建立進程錯誤
        case -1:
            throw new Exception('fork子進程失敗!');
            break;

        // 子進程worker
        case 0:
            $cpid = posix_getpid();
            cli_set_process_title("我是{$ppid}的子進程,個人進程id是{$cpid}.");

            // 執行業務腳本
            pcntl_exec('/usr/local/php/bin/php', $cmds[$i]);

            exit; // 這裏exit掉,避免worker繼續執行下面的代碼而形成一些問題
            break;
    }
}

// 等待子進程結束
while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo '子進程結束時間:'.date('H:i:s', time())."\n";
}

運行php index.php後:

clipboard.png

實例達到理想效果。

相關文章
相關標籤/搜索