PHP 多進程與進程間通信

PHP 多進程與進程間通信 (Inter-Process Communication)

參考文章:PHP 的多進程-IPC 之共享內存

由於業務需要,常常會遇到大量的數據需求,特別是在業務有分庫分表或是單表含有大量數據的時候,整體的數據需求又需要掃描全量數據,使用 PHP 單進程雖然能達到目標但是耗費的時間太長,這時候可能需要用到多進程以及進程間通信來滿足需求

PHP 如何實現多進程

pcntl

通常情況下,我們使用 cli 的方式執行 PHP 腳本。在 cli 模式下,我們可以使用 pcntl 擴展來實現 PHP 多進程。pcntl_fork() 函數可以在當前進程當前位置產生分支(子進程)。fork 是創建了一個子進程,父進程和子進程都從 fork 的位置開始向下繼續執行,不同的是 父進程 執行過程中,得到的 fork 返回值爲子進程號,而子進程得到的是 0

多進程流程圖:

父進程 fork_pcntl
子進程, pid = 0
父進程, pid = 子進程號
子進程, pid = 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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章