webman event_loop 使用了swoole,无法平滑重启或者平滑关闭

speedy

问题描述

webman 框架
event_loop 使用了swoole
无法平滑重启或者平滑关闭

程序代码或配置

php start.php stop -g
Workerman[start.php] stop 
Workerman[start.php] is gracefully stopping ...
config/server:
'event_loop' => \Workerman\Events\Swoole::class,
app/bootstrap:
Swoole\Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]);

在控制器中使用了协程代码后,使用平滑重启或者关闭-g参数,命令行就会一直等待,请求也进不来

代码:
go(function(){
    sleep(20);
    echo "xxx";
});

操作系统环境及workerman/webman等具体版本

ubuntu 22
php 8.3.13
Webman-framework v1.5.2

270 1 1
1个回答

本地测试了下,没发现问题
workerman/webman-framework 1.5.24
workerman/workerman 4.1.16

  • speedy 7天前

    感谢大佬回答,可能是我描述的还不够清除。我又重新安装了一遍,确实存在无法平滑关闭/重启问题。
    步骤如下:
    1.使用 composer 安装 webman
    2.修改 config/server.php 中的event_loop为 \Workerman\Events\Swoole::class
    3.新建 app/bootstrap/SwooleConfig.php
    4.在 SwooleConfig.php 文件中加入 Swoole\Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); 开启协程
    5.在 IndexController.php

    加入协程代码
        go(function () {
        sleep(5);
        echo 123;
        });
    1. 使用一个终端执行 php start.php start

    2. 使用浏览器访问index,看到控制台正确异步输出 “123”

    3. 使用一个新的终端执行 php start.php stop -g 关闭平滑关闭

      php start.php stop -g
      Workerman[start.php] stop 
      Workerman[start.php] is gracefully stopping ...
      无响应,持续等待
    4. 如果不用浏览器访问index,没有执行过异步代码,是可以正常平滑关闭的,一旦访问了协程代码,就无法平滑关闭。

    5. 使用新终端执行 php start.php stop 不加入 -g 参数,如果协程暂未运行结束,j旧的启动终端就会提示:

      [FATAL ERROR]: all coroutines (count: 1) are asleep - deadlock!
      [Coroutine-9]
      #0 /app_project/webman/app/controller/IndexController.php(12): sleep()
      #1 [internal function]: app\controller\IndexController->app\controller\{closure}()

    11.使用新终端执行 php start.php stop 不加入 -g 参数,如果协程结束了,则是正常关闭

    抱歉大佬,删了又改几次回复,就是想排版清楚一些,方便大佬查看

  • speedy 7天前

    补充在第4到第5步中还有一步:

    在config/bootstrap.php,新增初始化类
    return [
        support\bootstrap\Session::class,
        support\bootstrap\LaravelDb::class,
        app\bootstrap\SwooleConfig::class, //新增
    ];
  • walkor 7天前

    初步测试判断是swoole的bug,swoole文档说调用 Swoole\Event::del() 后事件回调函数会释放,但是实际没有释放,导致连接对象一直没释放。
    workerman后面会发版本会兼容这个问题。

  • speedy 7天前

    好的,感谢大佬回答!
    因为我的 webman 项目已经在运行了,后续可以使用此命令跟新吗?
    composer update workerman/webman-framework

  • walkor 7天前

    要更新workerman

    composer require workerman/workerman
    这边尽快出个版本兼容下

  • speedy 7天前

    非常感谢大佬!

  • walkor 7天前

    好了, composer require workerman/workerman 试下

  • speedy 7天前

    感谢大佬,刚刚测试了,发现2个疑问:

    1. 使用了 php start.php stop -g 会立刻执行协程代码。
    go(function(){
            sleep(10);
            echo "xxx";   //使用了stop -g 会立刻输出,并未按上面的sleep 10秒后执行
     });
    1. 执行业务代码报错
    go(function(){
                sleep(10);
                echo "xxx";
    
                $filePath = runtime_path() . DIRECTORY_SEPARATOR . date("Ymd") . "debug.log";
                $folderPath = dirname($filePath);
                if (!is_dir($folderPath)) {
                    mkdir($folderPath, 0755, true); 
                }
                file_put_contents($filePath,"test", FILE_APPEND);
    });

    报错内容:

    使用了stop -g 会立刻输出,并未的sleep 10秒后执行,并且业务代码出错
    
    xxx
     [FATAL ERROR]: all coroutines (count: 1) are asleep - deadlock!
     [Coroutine-8]
    --------------------------------------------------------------------
    #0 /app_project/ffbox2/app/service/UserService.php(26): is_dir()
    #1 [internal function]: app\service\UserService->app\service\{closure}()
  • walkor 7天前

    sleep()会被信号打断,会立刻返回。
    stop -g原理是判断连接数是否为0,为0则认为可以安全关闭,并不会判断是否有swoole协程在运行。
    deadlock 这个目前不好解决,你可以在 vendor/workerman/workerman/Events/Swoole.php 的 destroy() 方法
    加一句slee(1);给协程退出一点时间试下

    sleep(1);
    Event::exit();
  • speedy 7天前

    非常感谢大佬,加入了sleep(1),测试十分有效,我添加了一千个协程,都可以安全退出和重启;
    大佬这个小改动会更新到compose中吗?

  • walkor 7天前

×
🔝