PHP 多進程與進程間通信 (Inter-Process Communication)
參考文章:PHP 的多進程-IPC 之共享內存
由於業務需要,常常會遇到大量的數據需求,特別是在業務有分庫分表或是單表含有大量數據的時候,整體的數據需求又需要掃描全量數據,使用 PHP 單進程雖然能達到目標但是耗費的時間太長,這時候可能需要用到多進程以及進程間通信來滿足需求
PHP 如何實現多進程
pcntl
通常情況下,我們使用 cli
的方式執行 PHP 腳本。在 cli
模式下,我們可以使用 pcntl 擴展來實現 PHP 多進程。pcntl_fork()
函數可以在當前進程當前位置產生分支(子進程)。fork 是創建了一個子進程,父進程和子進程都從 fork 的位置開始向下繼續執行
,不同的是 父進程
執行過程中,得到的 fork 返回值爲子進程號
,而子進程得到的是 0
多進程流程圖:
代碼實現:
// 多進程數量
$fork = 3;
for ($i = 0; $i < $fork; $i++) {
// 創建進程
$pid = pcntl_fork();
if ($pid > 0) {
// 父進程
echo "========== {$pid} ==========" . PHP_EOL;
} else if ($pid == 0) {
// 進入子進程, 處理業務邏輯
echo "========== {$pid} ==========" . PHP_EOL;
sleep(3);
// 子進程退出
exit();
} else {
exit('fork error.');
}
}
注意點:
- 子進程必須有退出,否則會產生殭屍進程
- 父進程在異常退出的時候,子進程也必須退出(這裏涉及到信號量的使用,後面講解)
PHP 的進程間通信
實現 PHP 進程間通信的方式有很多種,共享內存
,管道
,信號量
,信號
,消息隊列
,socket
此處我們通過共享內存的方式來實現:統計分庫分表系統中,所有命中條件的數據總數量
我們先來看幾個 PHP 爲我們提供的幾個與共享內存相關的系統內存函數
shm 系列函數
// 申請共享內存,返回共享內存資源號
$shm_id = shm_attach($key, 1024, 066);
// 設置共享內存的值,可以理解爲:爲某塊共享內存設置一個 key - value 的鍵值對值
$sha_key = 1;
shm_put_var($shm_id, $sha_key, $var);
// 獲取共享內存值
shm_get_var($shm_id, $sha_key);
// 檢測某個 key 是否存在
shm_has_var($shm_id, $sha_key);
// 刪除一個 key
shm_remove_var($shm_id, $sha_key);
// 移除共享內存塊
shm_remove($shm_id);
// 斷開共享內存的鏈接
shm_detach($shm_id)
shmop 系列函數
/**
* shmop_open(int $key , string $flags , int $mode , int $size)
* $key 共享內存的key
* $flags 的值有以下幾種
* a : 創建一個只讀的共享內存區。
* c : 如果共享內存區已存在,則打開該共享內存區,並嘗試讀寫。否則新建共享內存區
* w : 創建一個讀寫共享內存區
* n : 創建一個共享內存區,如果已存在,則返回失敗
*
* $mode 讀寫權限。如0755 0644 等
* $size 申請共享內存區的大小
*/
/**
* shmop_read( resource $shmid , int $start , int $count)
* 將從共享內存塊中讀取數據
* $shmid 共享內存id,資源類型
* $start 從共享內存的那個字節開始讀起
* $count 一次讀取多少個字節。
* 如果count值小於發送的信息長度,則信息會被截斷。
*/
/**
* shmop_write(resource $shmid , string $data , int $offset)
* 將數據寫入共享內存塊
* $data 將要寫入的數據
* $offset 從共享內存塊的那個位置開始寫入。
* 該函數的返回值是寫入數據的長度。
*/
/**
* shmop_size(resource $shmid);
* 返回當前共享內存塊,已經使用的大小
*/
/**
* shmop_delete ( resource $shmid )
* 刪除一個共享內存塊的,刪除引用關係
*/
/**
* shmop_close ( resource $shmid )
* 關閉共享內存塊
* 要先使用shmop_delete 之後才能繼續使用shmop_close
*/
共享內存通信具體實現
// 獲取一個唯一 id
$key = ftok(__FILE__, 't');
// 共享內存鍵名
$sha_key = 1;
// 創建一塊共享內存
$shm_id = shm_attach($key);
// 子進程 id 號數組
$childList = [];
for($i = 0; $i < 3; $i++) {
// 創建子進程
$pid = pcntl_fork()
if ($pid > 0) {
// 子進程號寫入數組
$childList[] = $pid;
} elseif ($pid == 0) {
// 子進程邏輯
if (!shm_has_var($shm_id, $sha_key)) {
// 不存在共享內存,初始化值
shm_put_var($shm_id, $sha_key, 0);
} else {
// 如果共享內存存在操作共享內存
$var = shm_get_var($shm_id, $sha_key);
$var++;
shm_put_var($shm_id, $sha_key, $var);
}
// 子進程退出
exit();
} else {
exit('fork error.');
}
}
// 最後被設置的值
$lastVar = shm_get_var($shm_key, $sha_key);
// 刪除共享內存
shm_remove($shm_id);
// 斷開共享內存鏈接
shm_detach($shm_id);