场景:
由于公司的邮件Smtp服务器,是基于swoole的进程池实现的。但是swoole进程池,并没有平滑重启功能,如果当你的业务还在worker中运行的时候,通过 SIGUSR1 或 SIGUSR2 信号重新启动所有的worker,这样会导致你的部分业务丢失。
解决方案:
swoole进程池 的底层仅设置了主进程(管理进程)的信号处理,并未对 Worker 工作进程设置信号,如果你在worker中进行了
信号监听,那么就可以实现worker 进程的平滑重启
* 工作进程为异步模式,请使用 [Swoole\Process::signal](https://wiki.swoole.com/#/process?id=signal) 监听信号
* 工作进程为同步模式,请使用 `pcntl_signal` 和 `pcntl_signal_dispatch` 监听信号
由于swoole的进程池是同步模式,故采用 pcntl_signal
$pool->on("WorkerStart", function (Pool $pool,int $workerId) {
swoole_set_process_name("mail:pool:{$workerId}"); // 设置进程别名
$redis = new Redis();
$redis->connect("127.0.0.1");
$redis->setOption(Redis::OPT_READ_TIMEOUT,-1);
$run=true;
/**
* 安装信号,便于调用
* 不会主动执行,由信号触发器 触发执行
* 发送 USR1 信号 会重启进程
* 发送 TERM 信号 会停止信号
*/
pcntl_signal(SIGTERM, function ($signo) use(&$run,$pool){
echo "pcntl收到 {$signo} 信号\n";
$run=false;
});
echo "进入redis->{$workerId}\n";
while($run){
if($redis->ping() != "+PONG"){
$redis = new redis;
$redis->connect("127.0.0.1");
$redis->setOption(redis::OPT_READ_TIMEOUT,-1);
}
$data = $redis->brPop("list:swoole",3);
/***
* 等待外部信号,并且调用信号。该函数有两个功能。
1 . 等待外部信号调用。2.触发信号。
备注: 该函数只有接收到外部信号时,才能触发信号
*/
pcntl_signal_dispatch();
$datas = $data[1] ?? "";
if($datas){
require "/data/wwwroot/php-cli/beanstalk.php";
co::sleep(20); // 代替业务逻辑等待
$log= "写入数据内容 : {$datas}\n";
echo $log;
file_put_contents("/data/wwwroot/php-cli/swoole.log",$log,FILE_APPEND);
}
}
});
守护进程启动程序,及检测启动工作进程是否正常
$ php test -d=true # 守护进程启动
$ ps -ef | grep php # 查看主进程是否启动运行 如果在运行可看到主进程号 Pid
# ps -ef | grep pool 通过进程别名可以看到更详细的信息
$ pstree -p Pid # 查看进程树 得到子进程的数量是否与进程池设定的数量是否相等,相等则表示正常
平滑重启动和停止
$ kill -USR1 Pid # 重启
$ kill -TERM Pid # 停止
网友评论