我在做非常轻量的任务调度服务的时候刚好顺带撸了一下PHP多进程相关的内容,总共写了不到300行代码,能够帮助理解workerman是如何进行多进程处理的;
已经做了一些测试用例,把基本上常规的操作都覆盖了,随后还会补充更多的测试做覆盖;
另外说一下,这玩意儿其实可以用在workerman/webman里,当然这种操作比较骚(不建议,毕竟这是进程不是线程);
可以进行进程的嵌套fork,当然这种操作也比较骚,也不是很建议,还是那句话,进程不是线程;
个人觉得代码写的还算是比较通俗易懂,结构也很清晰(毕竟没几行代码);
感觉其实亮哥可以用这个思路把workerman精简一下(逃
https://github.com/workbunny/process
这是一个基于ext-pcntl和ext-posix拓展的PHP多进程助手,用于更方便的调用使用。
composer require workbunny/process
// 使用对象方式
$p = new \WorkBunny\Process\Runtime();
$p->fork(function(){
var_dump('child');
});
$p = new \WorkBunny\Process\Runtime();
$p->parent(function(){
var_dump('parent'); # 仅输出一次
});
$p = new \WorkBunny\Process\Runtime();
$p->run(function(){
var_dump('child');
},function(){
var_dump('parent');
}, 4); # 1 + 4 进程
$p = new \WorkBunny\Process\Runtime();
$p->wait(function(\WorkBunny\Process\Runtime $parent, int $status){
# 子进程正常退出则会调用该方法,被调用次数是正常退出的子进程数量
},function(\WorkBunny\Process\Runtime $parent, $status){
# 子进程异常退出则会调用该方法,被调用次数是异常的子进程数量
});
注:作用范围为父Runtime的方法仅在父Runtime内有有效响应
方法名 | 作用范围 | 是否产生分叉 | 描述 |
---|---|---|---|
fork() | 父Runtime | √ | 分叉一个子Runtime |
run() | 父Runtime | √ | 快速分叉N个子Runtime |
wait() | 父Runtime | × | 监听所有子Runtime状态 |
parent() | 父Runtime | × | 为父Runtime增加回调响应 |
isChild() | 所有 | × | 判断是否是子Runtime |
getId() | 所有 | × | 获取当前Runtime序号 |
getPid() | 所有 | × | 获取当前RuntimePID |
getPidMap() | 父Runtime | × | 获取所有子RuntimePID |
number() | 父Runtime | × | 获取Runtime数量 or 产生子Runtime自增序号 |
setConfig() | 所有 且 分叉发生前 | × | 设置config |
getConfig() | 所有 | × | 获取config |
getPidMap() | 父Runtime | × | 获取所有子RuntimePID |
setPriority() | 所有 | × | 为当前Runtime设置优先级 需要当前执行用户为super user |
getPriority() | 所有 | × | 获取当前Runtime优先级 |
$p = new \WorkBunny\Process\Runtime([
'pre_gc' => true,
'priority' => [
0, // 主Runtime优先级为0
-1, // id=1的子Runtime优先级为-1
-2, // id=2的子Runtime优先级为-2
-3 // id=3的子Runtime优先级为-3
]
]);
在 fork 行为发生后,Runtime对象会产生两个分支
fork() 和 run() 之后的代码域会被父子进程同时执行,但相互隔离:
$p = new \WorkBunny\Process\Runtime();
$p->fork(function(\WorkBunny\Process\Runtime $runtime){
var_dump($runtime->getId()); # id !== 0
});
var_dump('parent'); # 打印两次
$p = new \WorkBunny\Process\Runtime();
$p->run(function (\WorkBunny\Process\Runtime $runtime){
},function(\WorkBunny\Process\Runtime $runtime){
}, 4);
var_dump('parent'); # 打印5次
$p = new \WorkBunny\Process\Runtime();
$p->fork(function(\WorkBunny\Process\Runtime $runtime){
var_dump($runtime->getId()); # id !== 0
var_dump('old-child');
$newP = new \WorkBunny\Process\Runtime();
$newP->fork(function(\WorkBunny\Process\Runtime $newP){
var_dump($newP->getId()); # id === 0
var_dump('new-parent');
});
});
# run 方法同理
$p = new \WorkBunny\Process\Runtime();
$p->run(function (){},function(){}, 4);
if($p->getId() === 3){
var_dump('im No. 3'); # 仅id为3的Runtime会生效
}
# fork同理
$p = new \WorkBunny\Process\Runtime();
$p->run(function (){},function(){}, 4);
if($p->isChild()){
var_dump('im child'); # 所有子Runtime都生效
}
# fork同理
$p = new \WorkBunny\Process\Runtime();
$p->run(function (){},function(){}, 4);
if(!$p->isChild()){
var_dump('im parent'); # 父Runtime都生效
}
# 或以注册回调函数来执行
$p->parent(function(\WorkBunny\Process\Runtime $parent){
var_dump('im parent');
});
# fork同理
$p = new \WorkBunny\Process\Runtime();
$p->fork(function(\WorkBunny\Process\Runtime $runtime){
var_dump($runtime->getId()); # id !== 0
});
$p->parent(function (\WorkBunny\Process\Runtime $runtime){
var_dump($runtime->getId()); # id === 0
});
$p->run(function (\WorkBunny\Process\Runtime $runtime){
var_dump($runtime->getId()); # id !== 0
},function(\WorkBunny\Process\Runtime $runtime){
var_dump($runtime->getId()); # id === 0
}, 4);
$p = new \WorkBunny\Process\Runtime();
$p->fork(function(\WorkBunny\Process\Runtime $runtime){
var_dump('child'); # 生效
$runtime->fork(function(){
var_dump('child-child'); # 由于fork作用范围为父Runtime,所以不生效
});
});
$p->parent(function (\WorkBunny\Process\Runtime $runtime){
var_dump('parent'); # 生效
$runtime->fork(function(){
var_dump('parent-child'); # 生效
});
});
# run 方法同理
注:该方法仅父Runtime生效
$p = new \WorkBunny\Process\Runtime();
var_dump($p->number(false)); # 仅父Runtime会输出
注:该方法可结合指定执行区别获取
$p = new \WorkBunny\Process\Runtime();
var_dump($p->getPid()); # 所有Runtime会输出
注:该方法仅父Runtime生效
注:该方法在会阻塞至所有子Runtime退出
$p = new \WorkBunny\Process\Runtime();
$p->wait(function(\WorkBunny\Process\Runtime $runtime, $status){
# 子Runtime正常退出时
}, function(\WorkBunny\Process\Runtime $runtime, $status){
# 子Runtime异常退出时
});
👍👍👍