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