Ratchet PHP内存泄漏问题

3
我在一些基于浏览器的游戏中使用Ratchet作为套接字服务器,我注意到了一个非常奇怪的行为。
我的应用程序类实现了WampServerInterface,我注意到在4-5个客户端通过autobahn.js连接和断开连接后,会有大约300KB的内存卡住。然后,如果另外7-8个客户端连接和断开连接,内存使用量不会增加。只有当10-12个新客户端连接和断开时,它才会增加,所以我有一种印象它正在重复使用内存,但我仍然担心当许多客户端连接到服务器时可能会导致内存泄漏。
然后我决定进行一些测试,我制作了一个实现MessageComponentInterface的应用程序类(这样我就可以使用telnet连接)。下面是启动服务器的代码:
<?php
ini_set('display_errors', 'On');

require 'vendor/autoload.php';
require 'bootstrap.php';

use Ratchet\Server\IoServer;
use AsterMedia\Games\Socket;

$server = IoServer::factory(
    new Socket(),
    9090,
    '127.0.0.1'
);

$server->run();

?>

我的应用程序类非常简单,看起来像这样:

    <?php

    namespace AsterMedia\Games;

    use Ratchet\MessageComponentInterface;
    use Ratchet\ConnectionInterface;

    use Symfony\Component\Console\Output\OutputInterface;

    class Socket implements MessageComponentInterface {

        public function onClose(ConnectionInterface $conn) {

            echo "Client disconnected" . $this->getMemoryUsage() . PHP_EOL;

        }

        public function onError(ConnectionInterface $conn, \Exception $e) {

            echo $e->getMessage() . PHP_EOL;
        }

        public function onOpen(ConnectionInterface $conn) {

            echo "Client connected" . $this->getMemoryUsage() . PHP_EOL;

        }

        public function onMessage(ConnectionInterface $from, $msg) {

            echo $msg . PHP_EOL;
        }

        private function getMemoryUsage() {

            return sprintf('[Memory usage (currently) %dKB/ (max) %dKB]', round(memory_get_usage(true) / 1024), memory_get_peak_usage(true) / 1024);
        }   


    }

最终,我编写了一个bash脚本来连接和断开连接,这个脚本会在一个无限循环中运行。
    while true
    do
    echo "connect"
    exec 3<>/dev/tcp/127.0.0.1/9090
    exec 3<&-
    echo "disconnect"
    sleep 1
    done

在运行bash脚本后,我注意到相同的行为 - 几个周期后,内存使用量增加了。这个问题是否与Ratchet(或React)有关,还是仅仅是PHP的问题?我忘记提到我使用启用了GC的PHP 5.5.3。

WampServerInterface,这是表示你在使用Windows系统吗?但是你的脚本表明了另外一种情况。最好也要指定一下你正在使用的操作系统。 - the
我不使用Windows,我使用Linux Mint 16和Apache2 Web服务器。当我说WampServerInterface时,我指的是http://socketo.me/api/class-Ratchet.Wamp.WampServerInterface.html。我使用WebSocket应用程序消息协议(WAMP)来包装我的应用程序类(类名为Socket)。我的应用程序类实现了WampServerInterface... 我相信这与操作系统无关。 - Milos Jovanovic
那是最初的设置,当我注意到这个内存问题时,我决定不使用WAMP,而只使用IoServer(http://socketo.me/docs/server),以确保即使我进行telnet连接时内存使用量也会增加。 - Milos Jovanovic
如果你喜欢PHP,你也可以尝试Truway(一个PHP WAMP v2实现)和Crossbar.io(一个WAMP v2路由器):http://crossbar.io/docs/Getting-started-with-PHP/ - 这是一个应用组件的样子https://github.com/crossbario/crossbar/blob/master/crossbar/crossbar/templates/hello/php/client.php - oberstet
谢谢您的建议,但我更愿意继续使用Ratchet,因为它具有一些功能,如SessionProvider(与我的Symfony2网站共享会话)等。 - Milos Jovanovic
2个回答

4

默认情况下,WampServer不会在主题为空时释放它们。Ratchet v0.3.2引入了一个新功能来改变这种情况。主题对象现在有一个叫做autoDelete的属性,如果设置为true,则当订阅者数量达到0时将销毁该主题。

public function onSubscribe(ConnectionInterface $conn, $topic) {
    $topic->autoDelete = true;
}

保持引用的原因最初是一个(糟糕的)设计决策。autoDelete被实现,而不是自动释放Topic,因为这种变化会导致向后兼容性问题和错误,如果用户代码保留了对Topic的引用(假设唯一的对象通过id)。

将autoDelete设置为true,一旦GC运行,您最终会注意到内存消耗下降。


只有一件事,我认为gc_collect_cycles()应该强制GC释放内存...您认为最适合放置该功能的地方是哪里(用于测试),以便我可以看到v0.3.2和autoDelete中的内存释放改进? - Milos Jovanovic
onClose 处理程序是运行 gc_collect_cycles() 的最佳位置。 - Tofandel

0

这是 PHP 流内部工作的方式。它们会缓存所有内容,一旦缓冲区增长,直到脚本关闭才会缩小。使用 unset() 可以清除缓冲数据,但缓冲区本身仍然存在(以防更多连接/消息到来)。它们可以变得非常大,并且如果您不限制帧大小(例如,如果有人尝试发送 1GB 文件),可能会成为安全/稳定性问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接