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);