报告bug:php8中workerman遇到异常会卡死不能重新fork一个子进程

dignfei

代码:

global $worker;
$worker = new Worker();
Worker::$pidFile = '/dev/shm/jmworker.pid';
$worker->onWorkerStart = function(){
    global $worker,$sec;
    $sec = 0.5;
    \Workerman\Lib\Timer::add($sec, function (){
        echo '333333333333333';
        //exit();
        1/0;
    },true);
};
// 运行worker
Worker::runAll();

遇到异常就会卡死不会退出进程,也不会重新fork一个子进程。执行exit()也不会、
比如数据库暂时无法连接,出现异常,就会导致子进程卡死不会退出,也不会重新fork一个子进程。

-------------------------------------- WORKERMAN --------------------------------------
Workerman version:4.0.19          PHP version:8.0.8
--------------------------------------- WORKERS ---------------------------------------
proto   user            worker           listen          processes    status
tcp     root            jmworker_main    none            1             [OK]
---------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
333333333333333DivisionByZeroError: Division by zero in /var/www/html/public/1.php:28
Stack trace:
#0 /var/www/html/vendor/workerman/workerman/Events/Event.php(167): {closure}(true)
#1 [internal function]: Workerman\Events\Event->timerCallback(-1, 1, Array)
#2 /var/www/html/vendor/workerman/workerman/Events/Event.php(195): EventBase->loop()
#3 /var/www/html/vendor/workerman/workerman/Worker.php(2430): Workerman\Events\Event->loop()
#4 /var/www/html/vendor/workerman/workerman/Worker.php(1554): Workerman\Worker->run()
#5 /var/www/html/vendor/workerman/workerman/Worker.php(1384): Workerman\Worker::forkOneWorkerForLinux(Object(Workerman\Worker))
#6 /var/www/html/vendor/workerman/workerman/Worker.php(1358): Workerman\Worker::forkWorkersForLinux()
#7 /var/www/html/vendor/workerman/workerman/Worker.php(542): Workerman\Worker::forkWorkers()
#8 /var/www/html/public/1.php(33): Workerman\Worker::runAll()
#9 {main}

Warning: EventBase::loop(): Failed to invoke event callback in /var/www/html/vendor/workerman/workerman/Events/Event.php on line 195

Warning: EventBase::loop(): Failed to invoke event callback in /var/www/html/vendor/workerman/workerman/Events/Event.php on line 195

Warning: EventBase::loop(): Failed to invoke event callback in /var/www/html/vendor/workerman/workerman/Events/Event.php on line 195
阅读 774
4个回答

six

event扩展不是稳定版本?

  • dignfei 2021-08-29

    3.0.5稳定版
    root@33e87e5e5ce1:/var/www/html# php --ri event

    event

    Event support => enabled
    Sockets support => disabled
    Debug support => disabled
    Extra functionality support including HTTP, DNS, and RPC => enabled
    OpenSSL support => disabled
    Thread safety support => disabled
    Extension version => 3.0.5
    libevent2 headers version => 2.1.11-stable
    root@33e87e5e5ce1:/var/www/html#

  • dignfei 2021-08-29

    你试了吗,运行一下那段代码,会不会卡死无法自动重启子进程?

dignfei

有没有大佬解答一下

  • MarkGo 2021-09-03

    看看php编译参数是不是有sigchild

  • dignfei 2021-09-05

    @2024: 使用的docker官方php镜像: docker pull php:8.0.10-fpm ,怎么看编译参数是不是有sigchild?

  • dignfei 2021-09-05

    @2024:你运行上面这段代码会不会报错?

  • MarkGo 2021-09-07

    @6279:我没使用workman,我是用webman,php8/php7 只要编译参数里包含了sigchild的话就会出现异常,php官方有人提bug了,你可以看看php -i|grep configure

  • dignfei 2021-09-08

    root@33e87e5e5ce1:/var/www/html# php -i|grep configure
    Configure Command => './configure' '--build=x86_64-linux-gnu' '--with-config-file-path=/usr/local/etc/php' '--with-config-file-scan-dir=/usr/local/etc/php/conf.d' '--enable-option-checking=fatal' '--with-mhash' '--with-pic' '--enable-ftp' '--enable-mbstring' '--enable-mysqlnd' '--with-password-argon2' '--with-sodium=shared' '--with-pdo-sqlite=/usr' '--with-sqlite3=/usr' '--with-curl' '--with-openssl' '--with-readline' '--with-zlib' '--with-pear' '--with-libdir=lib/x86_64-linux-gnu' '--enable-fpm' '--with-fpm-user=www-data' '--with-fpm-group=www-data' '--disable-cgi' 'build_alias=x86_64-linux-gnu'
    大佬,麻烦看一下包含了sigchild吗

William A

你代码有致命错误,退出才是正常行为。workerman 在你进程出现致命错误之后就会重新 fork 一个进程的,但是你的致命错误又没有合理地处理,就会重复以上步骤。

  • dignfei 2021-09-08

    对啊,遇到错误之后应该重新fork一个进程,但是我贴出来的代码不会重新fork一个进程。你测试了代码吗

  • William A 2021-09-08

    @6279:当然试过。每次 ps 查到的子进程id都不一样了

  • William A 2021-09-08

    @6279:补充一下。我用的是 React EventLoop,你可能用的是 event。刚刚对比了一下,react 的 eventloop 的 add 方法,临时想到了两个解决方法。
    一、修改 Events/EventInterface.php 中的 add 方法修改为 public function add($fd, $flag, $func, array $args = []); 再将 Events/Event.php 中的 add 方法签名修改为 public function add($fd, $flag, $func, array $args = []);
    二、自己在你的定时器中加上 try {} catch (Throwable $e) { echo $e->getMessage(), "\n"; },注意php7之后才可以 catch 错误,跟异常不一样的;

  • William A 2021-09-08

    @6279:还是用 try catch 吧,另外一个不行

  • 静默 2021-09-10

    看起来是event扩展的bug,估计event扩展在php8下还没完善

  • dignfei 2021-09-13

    @7304: 牛逼,你是修改了workerman的源代码替换成React EventLoop吗?

MarkGo

我剛測試了,
同樣配置下,
php7 + event + libevent2 表現正常。
php8 + event + libevent2 表現異常。
其中event和libevent2 是 相同版本的。
而且問題只有在Timer裡才會發生,
我是在FileMonitor裡,
實例化時添加1/0;
這時候php7/8 都會自動重啟。
如果在實例化添加Timer::add.....,
php7 會重啟,
php8 會一直報錯Failed to invoke event callback。

補充下:
opcache開啟關閉都試過,結果一樣。
event

Event support => enabled
Sockets support => enabled
Debug support => disabled
Extra functionality support including HTTP, DNS, and RPC => enabled
OpenSSL support => disabled
Thread safety support => disabled
Extension version => 3.0.5
libevent2 headers version => 2.1.8-stable

如果使用select模型不會出錯,只有event的timer才會異常,
感覺是event的問題。

還是使用php7.4,有高版本要求的就把event轉為select模型吧。

  • 静默 2021-09-10

    看起来是event扩展的bug,估计event扩展在php8下还没完善

  • dignfei 2021-09-13

    大佬牛逼,swoole不用event扩展也会这样,不知道是不是php8的bug