pcntl信号机制配合swoole进程池 不影响业务平滑重启子进

2020-03-05  本文已影响0人  骑蚂蚁上高速_jun

场景:
由于公司的邮件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 # 停止
上一篇下一篇

猜你喜欢

热点阅读