SSE非常慢

Jam*_*ald 7 html javascript php server-sent-events eventsource

我目前正在为网页游戏编写通信框架,通信地图如下所示: 代码如下:

test.php的:

<!DOCTYPE html>
<html>
    <head>
        <title> Test </title>
        <script>
            function init()
            {
                var source = new EventSource("massrelay.php");
                source.onmessage = function(event)
                {
                    console.log("massrelay sent: " + event.data);
                    var p = document.createElement("p");
                    var t = document.createTextNode(event.data);
                    p.appendChild(t);
                    document.getElementById("rec").appendChild(p);
                };
            }

            function test()
            {
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function () 
                {
                    if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) 
                    {
                        console.log("reciver responded: " + xhr.responseText);
                    }
                }
                xhr.open("GET", "reciver.php?d=" + document.getElementById("inp").value , true);
                xhr.send();
                console.log("you sent: " + document.getElementById("inp").value);
            }
        </script>
    </head>
    <body>
        <button onclick="init()">Start Test</button> 
        <textarea id="inp"></textarea>
        <button onclick="test()">click me</button>
        <div id="rec"></div>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

这需要用户输入(当前是用于测试的文本框)并将其发送到接收器,并写回接收器响应控制台的内容,我从未收到接收器的错误.它还为发送的SSE添加了一个事件监听器.

reciver.php:

<?php 
    $data = $_REQUEST["d"];
    (file_put_contents("data.txt", $data)) ? echo $data : echo "error writing";
?>
Run Code Online (Sandbox Code Playgroud)

您可以看到这非常简单,只有在发送回写成功之前才将数据写入data.txt.data.txt只是将"管"数据传递给massrelay.php.

massrelay.php:

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    while(1)
    {
        $data = file_get_contents("data.txt");
        if ($data != "NULL")
        {
            echo "data: " . $data . "\n\n";
            flush();
            file_put_contents("data.txt", "NULL");
        }
    }
?>
Run Code Online (Sandbox Code Playgroud)

massrelay.php检查data.txt中是否有任何数据,如果有,将使用SSE将其传递给任何有事件监听器的人,一旦读取数据,它将清除数据文件.

整个事情实际上是完美的,除了轻微的ishue可能需要30秒到10分钟,massrelay.php从数据文件发送数据.对于网络游戏,这是完全不可接受的,因为您需要实时操作.我想知道是否由于我的代码存在缺陷或者如果不是我正在思考硬件而花费这么长时间(我自己在戴着sempron的2006戴尔主持).如果有人发现任何问题,请让我知道谢谢.

如果您有兴趣自己看测试,可以在这里找到

Dar*_*ook 6

我在您的代码中看到了三个问题:

  • 没睡
  • 没有ob_flush
  • 届会

您的while()循环不断读取文件系统。您需要放慢速度。我在下面睡了半秒钟;以最大值接受可接受的延迟进行实验。

PHP具有自己的输出缓冲区。您可以@ob_flush()用来刷新它们(@抑制错误)并flush()刷新Apache缓冲区。两者都是必需的,顺序也很重要。

最后,PHP会话锁定,因此,即使您的客户端可能正在发送会话cookie,即使您的SSE脚本不使用会话数据,也必须在进入无限循环之前关闭会话。

我在下面的代码中添加了所有这三个更改。

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    session_write_close();
    while(1)
    {
        $data = file_get_contents("data.txt");
        if ($data != "NULL")
        {
            echo "data: " . $data . "\n\n";
            @ob_flush();flush();
            file_put_contents("data.txt", "NULL");
        }
        usleep(500000);
    }
Run Code Online (Sandbox Code Playgroud)

顺便说一句,在另一个答案中关于使用内存数据库的建议是好的,但是文件系统的开销以毫秒为单位,因此它不能解释“ 30秒到10分钟”的延迟。


Mac*_*ity 5

我不知道写一个平面文件是最好的方法.文件I/O将成为你的主要瓶颈(阅读之上的阅读意味着你将很快达到最大值).但假设你想继续这样做......

您的应用程序可以从PHP会话中受益,以存储一些数据,因此您不会等待I/O. 这是MemcachedRedis等中间软件也可以帮助您的地方.你要做的是将数据存储reciver.php在文本文件中并将其写入内存缓存(或将其放入写入内存存储的会话中).这使得检索非常快速并减少了文件I/O.

我强烈建议你的数据数据库.MySQL尤其会将通常访问的数据加载到内存中以加快读取操作.

  • @Peter对于一个简单的评论来说,它有点过于宽泛.如果没有构建完整的工作模型,就无法构建代码示例 (2认同)