Swoole如何在使用了Server后实现一个可优雅退出的while(true)

前言

目前使用swoole的Server做了一个TCP的服务端,然后产生了一个比较特殊的需求,需要某几个进程去长时间while(true)去执行任务。

目前的方案

  1. swoole提供的task进程,在onwoker事件直接进入while循环,然后执行任务
  2. swoole提供的task进程,在进程启动完毕后,使用worker0进程投递任务到task进程,然后触发task内的while循环.
  • 以上是目前试过的2个方案.
  • 但是,测试发现1和2都无法响应SIGTERM与SIGUSR1,因为业务死循环了,swoole发了信号给子进程,等待退出,但是子进程在死循环.
  • 于是,又加上了pcntl扩展设置了一个异步信号,当监听到信号时将某个变量置为0 然后while检查到0就退出.

遇到了的问题

  • 经过测试,发现while成功退出了,数据也收回了,没有接下来的业务代码了,但是swoole的主进程却没有收回进程。【不确定是否为pcntl扩展导致信号覆盖造成的bug所以暂时没有提交】
  • 发现这个问题后,主动使用SIGKILL对自身进行了自杀,然后发现swoole成功拉起了新的进程去做工
  • 看似一切都解决了没有问题,但是没想到在进程持续工作20分钟后突然会出现假死,又或者重载后进程执行一次任务直接假死,反复几次重载后又不假死

求知

  1. 有其它可以执行while死循环又可以优雅跟随信号退出的方案吗?(另写一个专门的进程组去做任务然后再通信给serv的的做法很不符合现有需求,所以不考虑)
  2. 到底为什么会卡住?(不会是业务代码的问题,业务代码测试过只echo 一句话也会复现)
  3. 卡住之后发送SIGTERM能够结束服务,但是再次运行会发现先输出了上次卡住之后的阻塞内容,然后才输出了系统正常启动后的内容,这是为什么呢
这个疑问是源自需要while死循环,所以设置投递抢占然后去投递的概念没必要有,上面的投递任务去触发while只是单纯为了进入task然后不断检查执行,而不是为了投递,是测试进程卡住排除错误使用才那样干了一次
if($worker_id == 0){
    go(function() use($serv){
        for ($i=0; $i < SYSTEM_TASK; $i++) { 
            $serv->task('',$i);
        }
    });
}

只是为了进入task...

阅读 889
评论
    2 个回答

    补充个强行优雅的方案

    在workerstart事件判断是谁

    start.php

        if ($worker_id >= $serv->setting['worker_num']) {
            //task
            require_once('task.php');
        } else {
            //worker
        }

    task.php

    uf($worker_id){
        //根据id来进行指定进程的长时间业务
        while(true){
            //用Pcntl监听信号
            //收到消息break
        }
    }
    //用Posix杀死这个进程 kill SIGKILL

    代码只是纯思路 我的业务是纯类实现的 所以贴了也没看的意义 按这个逻辑就不会有什么问题了 至于为什么假死 忘了 有空补 给个任务次数重启进程也可以解决大部分假死问题

      你需要在 for 循环中加入一个条件判断,在一定情况下退出循环。

      如果是跨进程操作可以使用 Atomic 模块

        撰写回答

        登录后参与交流、获取后续更新提醒