SWOOLE PROCESS多进程模型
<?php
class SwooleProcessController {
//主进程pid
protected $pidfile = "";
//构造函数
public function __construct(){
$this->pidfile = LOG_PATH."swoole/consumer_master.pid";
}
//初始化一些参数
public function start(){
//命令
$cmd = $_SERVER['argv'][2] ?? "start";
//woker数量
$workernum = $_SERVER['argv'][3] ?? 1;
//daemon
$daemon = in_array('-d',$_SERVER['argv'],true);
//基于命令
switch($cmd){
case "start":
$this->startmain($daemon,$workernum);
break;
case "stop":
$this->stop();
break;
case "status":
$this->status();
break;
default:
echo "wrong cmd";
exit(1);
}
}
/**
* 启动进程
* @param int $daemon 是否守护
* @param int $workernum worker数量
*/
public function startmain($daemon,$workernum){
if($this->is_running()){
echo "Already running.PID=".trim(file_get_contents($this->pidfile)) . PHP_EOL;
return;
}
//守护进程
if($daemon){
\Swoole\Process::daemon(true,true);
}
//leader
posix_setsid();
//主进程ID
$masterpid = posix_getpid();
file_put_contents($this->pidfile,(string)$masterpid);
//设置主进程名称
swoole_set_process_name("php:consumer master");
$running = true;
$workers = [];
//设置异步信号监听
pcntl_async_signals(true);
pcntl_signal(SIGTERM,function() use (&$running,&$workers){
debugLog("Master recv SIGTERM, worker count=".count($workers), 'swoole/consumer.log');
$running = false;
\Swoole\Timer::clearAll();
foreach($workers as $pid=>$_){
debugLog("kill child pid: {$pid}",'swoole/consumer.log');
\Swoole\Process::kill($pid,SIGTERM);
}
});
pcntl_signal(SIGINT, function() use (&$running, &$workers) {
$running = false;
foreach ($workers as $pid => $_) {
\Swoole\Process::kill($pid, SIGTERM);
}
});
//\Swoole\Timer::tick(1000, function(){});
//\Swoole\Event::wait();
//启动子进程worker
for($i=0;$i<$workernum;$i++){
$this->startworker($workers,$i);
}
//处理子进程异常退出
\Swoole\Process::signal(SIGCHLD,function() use (&$workers,&$running){
while($ret = \Swoole\Process::wait(false)){
$pid = $ret['pid'];
$code = $ret['code'];
$sig = $ret['signal'];
unset($workers[$pid]);
if($running){
$id = $ret['pid'] % 10000;
$this->startworker($workers,$id);
}
}
});
//主进程事件循环
while($running){
pcntl_signal_dispatch();
sleep(1);
}
//等待子进程退出
$waitStart = time();
while(!empty($workers) && time() - $waitStart < 10){
sleep(1);
}
@unlink($this->pidfile);
debugLog("Master Stoppd.","swoole/consumer.log");
}
/**
* 记动一个子进程
*/
public function startworker(&$workers,$workerid):void{
$process = new \Swoole\Process(function(\Swoole\Process $proc) use ($workerid){
swoole_set_process_name("php:consumer worker#{$workerid}");
//worker主进程
$this->workerMain($workerid);
},false,0,true);
$pid = $process->start();
$workers[$pid] = $process;
debugLog("Worker started. id={$workerid}, pid={$pid}\r\n",'swoole/consumer.log');
}
/**
* worker 主进程
*/
public function workerMain($workerid){
$running = true;
pcntl_async_signals(true);
$objredis = Apps::redis('cms');
\Swoole\Process::signal(SIGTERM, function() use (&$running, $objredis) {
$running = false;
$objredis->close(); // 立即唤醒 brPop
});
\Swoole\Process::signal(SIGINT, function() use (&$running, $objredis) {
$running = false;
$objredis->close();
});
while($running){
try {
$result = $objredis->brPop('testswoolekey', 5);
if(empty($result)){
continue;
}
debugLog($result[1], 'swoole/data.log');
} catch (\Exception $e) {
if (!$running) break;
usleep(20000);
}
}
debugLog("Worker {$workerid} exited", 'swoole/consumer.log');
}
//停止
public function stop(){
if(!$this->is_running()){
echo "Not running";
return ;
}
$pid = (int)trim(file_get_contents($this->pidfile));
$pgid = posix_getpgid($pid);
if($pgid > 0){
echo "Stop signal sent to PGID={$pgid}\r\n";
posix_kill(-$pgid,SIGTERM);
} else {
posix_kill($pid,SIGTERM);
echo "Stop signal sent to master PID={$pid}\r\n";
}
//等待退出
$timeout = 10;
while($timeout-- > 0){
if($pgid > 0){
if(!posix_kill($pid,0)) break;
} else {
if (!posix_kill($pid, 0)) break;
}
sleep(1);
}
if($pgid > 0){
posix_kill(-$pgid, SIGKILL);
echo "Force killing PGID={$pgid}\n";
} else {
\Swoole\Process::kill($pid,SIGKILL);
echo "Force killing master PID={$pid}\r\n";
}
@unlink($this->pidfile);
}
public function stop2()
{
if (!$this->is_running()) {
echo "Not running\n";
return;
}
$pid = (int)trim(@file_get_contents($this->pidfile));
if ($pid <= 0) {
echo "Invalid PID file\n";
return;
}
$pgid = @posix_getpgid($pid);
if ($pgid > 0) {
echo "Sending SIGTERM to process group PGID={$pgid}\n";
posix_kill(-$pgid, SIGTERM);
} else {
echo "Sending SIGTERM to master PID={$pid}\n";
posix_kill($pid, SIGTERM);
}
// 等待进程优雅退出
$timeout = 10;
$graceful = false;
while ($timeout-- > 0) {
if (!posix_kill($pid, 0)) {
$graceful = true;
break;
}
echo "Waiting for process to exit... ({$timeout}s left)\n";
sleep(1);
}
// 检查是否仍在运行
if (!$graceful) {
echo "Process did not exit in time, sending SIGKILL...\n";
if ($pgid > 0) {
posix_kill(-$pgid, SIGKILL);
} else {
posix_kill($pid, SIGKILL);
}
// 再次确认是否真的被杀掉
sleep(1);
if (posix_kill($pid, 0)) {
echo "Warning: process still alive after SIGKILL!\n";
} else {
echo "Process group successfully killed.\n";
}
} else {
echo "Process exited gracefully.\n";
}
// 最后安全删除 pid 文件
if (file_exists($this->pidfile)) {
@unlink($this->pidfile);
}
}
//状态
public function status(){
if(!$this->is_running()){
echo "not running";
return;
}
$pid = (int)trim(file_get_contents($this->pidfile));
echo "Running:Master PID={$pid}";
}
public function is_running():bool {
if(!is_file($this->pidfile)) return false;
$pid = (int)trim(@file_get_contents($this->pidfile));
return $pid>0 && posix_kill($pid,0);
}
}
基本实现进程启动,停止,进程数量,状态,主进程子进程信号监听
PHP中常见的问题点,知识点,及盲点。

浙公网安备 33010602011771号