PHP 脚本不会在 exit() 时终止

PHP script doesn't terminate upon exit()

提问人:Zhiyong Li 提问时间:8/26/2023 最后编辑:Zhiyong Li 更新时间:8/26/2023 访问量:70

问:

我们试图在出现致命错误时终止 PHP 脚本,以便 Supervisor 可以重新启动该脚本。以下是我们的异常处理程序:

    public function exceptionHandler(\Throwable $e)
    {
        if(Config::get('APP_DEBUG')) {
            echo (date("Y-m-d H:i:s").", uncaught exception in WsApp: ".$e->getMessage().PHP_EOL."");
        } else {
            \Sentry\captureException($e);
        }

        //exec('kill -9 ' . getmypid());
        exit("tried to exit from WsApp\r\n");
    }

PHP脚本依赖于MySQL服务器。如果我在运行此PHP脚本之前停止MySQL服务器,则会在终端中获得以下输出:

2023-08-25 20:18:13, exception happened when trying to setupConn, SQLSTATE[HY000] [2002] No such file or directory
2023-08-25 20:18:13, uncaught exception in WsApp: Failed to setup connection to local DB.
tried to exit from WsApp // indication that error handler actually ran
^C // script continued to run until terminated by Ctrl + C

似乎错误处理程序中的退出并没有真正终止脚本本身,直到我在键盘上按 Ctrl + C。

以下是我们的代码,在无法与数据库服务器建立连接时抛出异常:

        try {
            $this->dbConn = new \PDO($dsn, Config::get('DB_USER_BI'), Config::get('DB_PW_BI'), $options);
        } catch (\Throwable $th) {
            if(Config::get('APP_DEBUG')) {
                echo date("Y-m-d H:i:s").", exception happened when trying to setupConn, ".$th->getMessage().PHP_EOL."";
            }
            // If a local DB connection cannot be established
            // Then there is no point to run any script
            throw new \Exception('Failed to setup connection to local DB.');
        }

但是,正如另一篇文章所建议的那样,以下行实际上可以完成这项工作。

exec('kill -9 ' . getmypid());

我在终端中得到以下输出:

2023-08-25 20:16:14, exception happened when trying to setupConn, SQLSTATE[HY000] [2002] No such file or directory
2023-08-25 20:16:14, uncaught exception in WsApp: Failed to setup connection to local DB.
Killed // output of exec(...)

我的问题是,为什么 exit() 不终止脚本?我的理解是退出将终止整个过程。

另外,运行以下行会有什么后果?内存泄漏,因为类析构函数无法运行?

exec('kill -9 ' . getmypid());

请指教,谢谢。

PHP 异常 zeromq

评论

1赞 Barmar 8/26/2023
我的猜测是它正在尝试关闭与数据库的连接,并且这是挂起的。
0赞 Barmar 8/26/2023
如果可以避免,请不要使用。只需使用 .这将发送与键入 Ctrl-C 相同的信号。kill -9kill
0赞 Zhiyong Li 8/26/2023
嗨,@Barmar感谢您的快速回复和建议。我只是包含了我们的代码来设置数据库连接。您认为即使没有建立连接,PDO也会尝试关闭连接吗?
1赞 Barmar 8/26/2023
您可能需要使用系统调用跟踪来查看它卡住的位置。
1赞 Alberto Fecchi 8/26/2023
也许是 ZeroMQ 由于某些原因仍然挂起。您是否尝试过为(之前)设置值?像这样的东西 php.net/manual/en/class.zmq.php#zmq.constants.sockopt-lingerZMQ::SOCKOPT_LINGERconnect()$socket->setSockOpt(ZMQ::SOCKOPT_LINGER, 10);

答:

1赞 Zhiyong Li 8/26/2023 #1

在注释掉脚本中的各种代码块,然后移动它们后,问题被追踪到与 ZMQ 相关的问题。

如果在下面的第 3 行代码之前调用用于设置数据库连接的代码:

$gwLoop = React\EventLoop\Loop::get();
$gwContext = new Context($gwLoop);
$msgAckPuller = $gwContext->getSocket(ZMQ::SOCKET_PULL);

然后 exit() 按预期工作。 如果设置数据库连接的代码在第 3 行代码或类似以下的任何代码之后调用:

getSocket(ZMQ::SOCKET_PULL);

然后 exit() 将挂起。

在任意数量的代码行后调用数据库连接代码

getSocket( ZMQ::SOCKET_PUSH)

似乎触发 exit() 工作正常。

设置 ZMQ::SOCKOPT_LINGER 似乎并不能阻止 exit() 挂起:

$msgAckPuller->setSockOpt(ZMQ::SOCKOPT_LINGER, 10);

我对 ZMQ 和 ReactPHP 的了解有限,所以我只是关闭问题并将上面的信息留给将来参考。

我用最少的测试代码向 GitHub 上的 React 存储库报告了这个问题。

use React\ZMQ\Context;

require dirname(__DIR__).'/vendor/autoload.php';

$testLoop = React\EventLoop\Loop::get();
$testContext = new Context($testLoop);

$testPusher = $testContext->getSocket(ZMQ::SOCKET_PUSH);
$testPusher->bind('tcp://127.0.0.1:5555');

//exit("exited after ZMQ:SOCKET_PUSH / before ZMQ::SOCKET_PULL\r\n");

$testPuller = $testContext->getSocket(ZMQ::SOCKET_PULL);

exit("tried to exit after ZMQ::SOCKET_PULL\r\n");

$testPuller->bind('tcp://127.0.0.1:5557');

感谢大家参与讨论。