PHP 7.1 PECL-Event 在怪异的情况下悬挂



基于此答案,我已切换到pecl-event库。现在我有:

[root]# php -v
PHP 7.1.12 (cli) (built: Nov 22 2017 08:40:02) ( NTS ) Copyright (c) 1997-2017 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologieswith Zend OPcache v7.1.12, Copyright (c) 1999-2017, by Zend Technologies 
[root]# php --info | grep event
/etc/php.d/event.ini event libevent2 headers version => 2.1.8-stable
[root]# pecl list
Installed packages, channel pecl.php.net:
=========================================
Package Version State
event   2.3.0   stable

下面的示例表现奇怪。如果$loop->run()runme()函数的内部调用,则可以调用并调用回调。但是,如果$loop->run()是从runme()外部调用的,则将悬挂!

require_once __DIR__.'/../vendor/autoload.php';
$inner = count($argv) > 1;
$loop = new ReactEventLoopExtEventLoop();
//$loop = new ReactEventLoopStreamSelectLoop();
runme($loop, $inner);
if (!$inner) {
    echo "Outer startn";
    $loop->run();
}
function runme(ReactEventLoopLoopInterface $loop, $inner)
{
    $contextOpts = [];
    $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
    $context = stream_context_create($contextOpts);
    $socket = stream_socket_client('tcp://127.0.0.1:3306', $errno, $errstr, 0, $flags, $context);
    stream_set_blocking($socket, 0);
    $loop->addWriteStream($socket, function ($socket) use ($loop) {
        echo "done  ".(false === stream_socket_get_name($socket, true) ? 'false' : 'true')."n";
        $loop->removeWriteStream($socket);
    });
    if ($inner) {
        echo "Inner startn";
        $loop->run();
    }
    echo "Exit runmen";
}

运行结果:

[root@vultr Scraper]# php ./tests/test.php --inner
Inner start
done  false
Exit runme
[root@vultr Scraper]# php ./tests/test.php 
Exit runme
Outer start
...............HANGING HERE...........

我错过了什么,还是其中一个库/PHP的问题?有人在运行php7.1 react libevent的经验吗?

更新:=======================================================================

我确实使用了最新的" React/套接字"库" 0.8.6"测试。

require_once __DIR__.'/vendor/autoload.php';
$inner = count($argv) > 1;
$loop = new ReactEventLoopExtEventLoop();
$connector = new ReactSocketConnector($loop);
runme($loop, $connector, $inner);
if (!$inner) {
    echo "Outer startn";
    $loop->run();
}
function runme(ReactEventLoopLoopInterface $loop, ReactSocketConnector $connector, $inner)
{
    $connector->connect('tcp://127.0.0.1:3306')->
    then(function (ReactSocketConnectionInterface $conn) {
        echo ("Hello MySQL!n");
        $conn->close();
    },function ($e) {
        echo ("Bye MySQL!n");
    })->done();
    if ($inner) {
        echo "Inner startn";
        $loop->run();
    }
    echo "Exit runmen";
}

它正常工作并返回:

$ php ./testMysql.php 
Exit runme
Outer start
Hello MySQL!
$ php ./testMysql.php  --inner
Inner start
Hello MySQL!
Exit runme

但是,如果您进入 react socket tcpconnector :: waitforstreamonce(),并在下面的新承诺对象中删除$ canceller函数,而不是再次悬挂。看起来它在最新版本的反应中起作用,因为插座未存储明显的方式,并且实际上类似于v0.4.6中的代码。

中的代码
private function waitForStreamOnce($stream)
    {
        $loop = $this->loop;
        return new PromisePromise(function ($resolve, $reject) use ($loop, $stream) {
            $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject) {
                $loop->removeWriteStream($stream);
                // The following hack looks like the only way to
                // detect connection refused errors with PHP's stream sockets.
                if (false === stream_socket_get_name($stream, true)) {
                    fclose($stream);
                    $reject(new RuntimeException('Connection refused'));
                } else {
                    $resolve(new Connection($stream, $loop));
                }
            });
        });
    }

$ php ./testMysql.php  --inner
Inner start
.....HANGING
$ php ./testMysql.php 
Exit runme
Outer start
...HANGING

问题是 $socket 变量get get destained runme()返回时(就像任何本地PHP一样多变的!)。结果,在此套接字上打开的连接已关闭。

事件扩展可以最好地防止内存泄漏,因此如果可能的话,它不会存储对用户变量的引用。特别是,所有接受套接字资源(例如Event::__construct)的方法仅从输入变量中检索基础数字文件描述符。用户是实际上负责保留这些变量活着。。

以下脚本通过将$socket移至全局范围来解决问题。

require_once 'vendor/autoload.php';
$inner = count($argv) > 1;
$loop = new ReactEventLoopExtEventLoop();
$socket = init_socket();
runme($loop, $socket, $inner);
if (!$inner) {
    echo "Outer startn";
    $loop->run();
}
function init_socket()
{
    $contextOpts = [];
    $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
    $context = stream_context_create($contextOpts);
    $socket = stream_socket_client('tcp://test.local:80', $errno, $errstr, 0, $flags, $context);
    stream_set_blocking($socket, 0);
    return $socket;
}
function runme(ReactEventLoopLoopInterface $loop, $socket, $inner)
{
    $loop->addWriteStream($socket, function ($socket) use ($loop) {
        echo "done  ".(false === stream_socket_get_name($socket, true) ? 'false' : 'true')."n";
        $loop->removeWriteStream($socket);
    });
    if ($inner) {
        echo "Inner startn";
        $loop->run();
    }
    echo "Exit runmen";
}

在真实的应用程序中,您可能会将$socket存储为类成员变量。

嘿reactphp核心开发人员,只需查看您的脚本,我就可以在本地重现此脚本,这样我就可以为此提出问题(即使这可能超出了我们的范围)。

require_once __DIR__.'/../vendor/autoload.php';
$inner = count($argv) > 1;
$loop = new ReactEventLoopExtEventLoop();
//$loop = new ReactEventLoopStreamSelectLoop();
runme($loop, $inner);
    $contextOpts = [];
    $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
    $context = stream_context_create($contextOpts);
    $socket = stream_socket_client('tcp://127.0.0.1:3306', $errno, $errstr, 0, $flags, $context);
    stream_set_blocking($socket, 0);
    $loop->addWriteStream($socket, function ($socket) use ($loop) {
        echo "done  ".(false === stream_socket_get_name($socket, true) ? 'false' : 'true')."n";
        $loop->removeWriteStream($socket);
    });
    if ($inner) {
        echo "Inner startn";
        $loop->run();
    }
    echo "Exit runmen";

,但我建议您查看我们的套接字组件来处理连接。

看起来像:

$loop = new ReactEventLoopExtEventLoop();
$connector = new ReactSocketConnector($loop);
$connector->connect('tcp://127.0.0.1:3306')->then(function (ConnectionInterface $conn) use ($loop) {
    $conn->write("Hello MySQL!n");
});
$loop->run();

相关内容

  • 没有找到相关文章

最新更新