<?php
class TaskWorker
{
public $mpid = 0;
public $works = [];
public $max_precess = 4;
public $new_index = 0;
public function __construct()
{
try {
//设置进程名称
swoole_set_process_name(sprintf('php-ps:%s', 'master'));
//获取当前进程id,即主进程ID
$this->mpid = posix_getpid();
//创建子进程
$this->run();
//监听信号,回收僵尸进程,或者处理退出的进程,swoole这个是非阻塞的
$this->processWait();
}catch (\Exception $e){
die('ALL ERROR: '.$e->getMessage());
}
}
public function run()
{
for ($i = 0; $i < $this->max_precess; $i++) {
$this->new_index = $i;
//创建进程
$this->CreateProcess();
}
foreach ($processes as $process) {
$process->read()//读取子进程写的数据,这是阻塞操作,一定要全部子进程创建完成了再读
}
}
public function CreateProcess($index = null)
{
if(is_null($index)) {
$index = $this->new_index;
}
$process = new swoole_process(function(swoole_process $worker) use ($index) {
//创建子进程后执行的回调函数
//设置子进程的名称
swoole_set_process_name(sprintf('php-ps:%s', $index));
for ($j = 0; $j < 50; $j++) {
$this->checkMpid($worker);
$data = 'msg:'.$worker->read()."\r\n";
$worker->write($data);
sleep(1);
}
}, true);
$pid = $process->start()
$process->write("hello world")//向子进程写数据
$this->worker[$index] = $pid
$this->processes[$index] = $process
return $pid;
}
/**
* 判断主进程是否关闭,如果关闭,则子进程也关闭
* @param [type] &$worker [description]
* @return [type] [description]
*/
public function checkMpid(&$worker)
{
//获取父进程id
$ppid = posix_getppid();
$pid = posix_getpid();
// 检测父进程是否kill
if(!\swoole\process::kill($this->mpid, 0)){
//这句提示,实际是看不到的.需要写到日志中
echo "Master process {$ppid} exited,{$this->mpid} I also quit,current pid:{$pid}\n";
$worker->exit(0);
}
}
//重启退出的子进程
public function rebootProcess($ret)
{
$pid = $ret['pid'];
echo "ExitProcess: pid:".$pid.'exitcode:'.$ret['code'].PHP_EOL;
$index = array_search($pid, $this->works);
if(false !== $index){
$index = intval($index);
$new_pid = $this->CreateProcess($index);
echo "rebootProcess: {$index} = {$new_pid} Done" . PHP_EOL;
return;
}
throw new \Exception('rebootProcess Error: no pid' . PHP_EOL);
}
/**
* 异步监听信号
* @return [type] [description]
*/
public function processWait()
{
//监听主进程信息
swoole_process::signal(SIGTERM, function ($signo) {
echo "收到kill退出信号,退出主进程";
});
swoole_process::signal(SIGINT, function ($signo) {
echo "收到ctrl+c退出信号,退出主进程";
});
//监听子进程结束信号
swoole_process::signal(SIGCHILD, function ($signo) {
$ret = \swoole\process::wait();
if ($ret) {
$this->rebootProcess($ret);
}
});
}
}
new TaskWorker();
?>
swoole手册的demo有些问题。
需要注意的问题:
1.主进程中一定要执行$ret = \swoole\process::wait();会回收退出的子进程,但是这个方法会阻塞主进程,最好用3的方法。
2.在子进程中循环检测主进程是否kill, swoole_process::kill($main_pid, 0) 如果主进程kill然后子进程自动退出 $worker->exit(int $status=0);
3.回收子进程还可以用swoole_process::signal(SIGCHLD, function(){
$ret = swoole_process::wait(0) //回收子进程
});,SIGCHLD只是子进程在运行结束的时候产生的一i个信号,我们要想处理这个僵尸状态,最好就是父进程调用wait(),你可能有要问,既然都要用到wait,那抹干吗多此一举使用信号呢?首先要知道父进程调用wait以后处于阻塞状态,父进程不能干其他事情,使用效率降低,资源利用率低下,增加了开销,而调用信号以后,当子进程执行完毕以后,自动产再生一个信号给父进程,父进程收到信号以后就调用wait挥手子进程没有释放的资源。这样我的感觉就是子进程化被动为主动。父进程的工作也轻松了不少,可以做自己想做的事情。
4.主进程退出也可以监听
swoole_process::signal(SIGTERM, function(){
echo ‘程序退出’
});
5.处理子进程传出的数据时,$process->read()是阻塞读取,一定要把子进程全部创建完全之后在读取。
参考链接:https://blog.csdn.net/qq_18358973/article/details/91982638