【问题标题】:Gracefully close ReactPhp app优雅地关闭 ReactPhp 应用程序
【发布时间】:2015-07-30 10:53:33
【问题描述】:

这个问题与Consume only N messages from RabbitMQ with react\stomp, ack them separately and then exit 非常相关,但更笼统一些。

例如,我有一个缓慢的 I/O 操作:

$port = 4000;

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$socket->on('connection', function ($conn) use ($loop){

    $conn->on('data', function ($data) use ($conn, $loop) {

        if ($data == 42) {
            // this instantly stop the loop
            $loop->stop();
        }

        $process = new React\ChildProcess\Process('sleep 5; echo ' . $data);

        $loop->addTimer(0.001, function($timer) use ($process, $conn) {
            $process->start($timer->getLoop());

            $process->stdout->on('data', function($output) use ($conn) {
                if ($output) {
                    $conn->write("> $output");
                }
            });
        });
    });
});
echo "Socket server listening on port $port.\n";
echo "You can connect to it by running: telnet localhost $port\n";
$socket->listen($port);
$loop->run();
echo "exited";

当我运行$loop->run(); 时,我想在某个时候停止它,例如通过计时器,在接受 N 个请求后,或任何其他事件,如pcntl_signal,或数据断言,如示例中所示。

挑战是在退出之前完成所有开始的工作,我不知道如何实现它。

在我的服务器控制台中:

Socket server listening on port 4000.
You can connect to it by running: telnet localhost 4000
exited

在我的客户端控制台中:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
1
2
3
4
5
> 1
> 2
42
Connection closed by foreign host.

1、2、3、4、5 以 1 秒的间隔输入

相反,我希望看到这样的东西:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
1
2
3
4
5
> 1
> 2
42
> 3
6
> 4
> 5
Connection closed by foreign host.

【问题讨论】:

  • 根据你的处理方式,你需要一个可以访问$loop的回调,然后简单地调用$loop->stop(),我相信
  • @EliasVanOotegem: $loop->stop() 停止循环。我已经更新了问题以使其更加清晰,我需要循环在退出脚本之前完成所有已启动的作业。
  • 所以先断开套接字,当队列为空时——停止循环,进程将退出。但是,您的进程不会挂起的保证在哪里?
  • @N.B.谢谢,这是有道理的。您知道如何检查队列是否为空吗?一个进程可以随时挂起,它不是主体。可以在 SIGKILL 上立即退出。挑战是在 SIGTERM 上优雅地退出。

标签: php reactphp


【解决方案1】:

正如 cmets 中提到的,要优雅地退出,您需要跟踪正在运行的进程并仅在所有进程完成时才停止循环:

$socket->on('connection', function ($conn) use ($loop) {
    $processes = new SplObjectStorage();
    $stop = false;

    $conn->on('data', function ($data) use ($conn, $loop, $processes, &$stop) {
        if ('42' === trim($data)) {
            $stop = true;
            if (!$processes->count()) {
                $loop->stop();
            }
        }

        if ($stop) {
            return;
        }

        $process = new React\ChildProcess\Process('sleep 5; echo ' . $data);
        $processes->attach($process);

        $process->on('exit', function () use ($process, $processes, &$stop, $loop) {
            $processes->detach($process);

            if ($stop && !$processes->count()) {
                $loop->stop();
            }
        });

        $loop->addTimer(0.001, function($timer) use ($process, $conn) {
            $process->start($timer->getLoop());

            $process->stdout->on('data', function($output) use ($conn) {
                if ($output && '42' !== trim($output)) {
                    $conn->write("> $output");
                }
            });
        });
    });
});

【讨论】:

  • 感谢尤金,这正是我最终得到的解决方法。我不相信数据处理函数是保存循环管理代码的正确位置,但似乎没有更好的方法 =(
  • 好吧,更好的方法是发出你自己的“完成”事件,并附加一个带有循环管理逻辑的回调。
猜你喜欢
  • 1970-01-01
  • 2021-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-11
  • 1970-01-01
  • 2016-06-09
相关资源
最近更新 更多