Search code examples
javascriptphphtmlserver-sent-eventseventsource

SSE incredibly slow


I am currently writing the communication framework for a web game, the communications map can be seen below: The code is as follows:

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>

This takes user input (currently a textbox for testing) and sends it to the receiver, and writes back what the receivers response to the console, i have never received an error from the receiver. it also adds an event listener for the SSE that is sent.

reciver.php:

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

This as you can see is very simple and only functions to write the data to data.txt before sending back that the write was successful. data.txt is simply the "tube" data is passed through to 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");
        }
    }
?>

massrelay.php checks if there is any data in data.txt and if so will pass it using SSE to anyone with an event listener for it, once it reads the data it will clear the data file.

The entire thing actually works perfectly except for the slight ishue that it can take anywhere from 30 seconds to 10 minutes for massrelay.php to send the data from the data file. For a web game this is completely unacceptable as you need real time action. I was wondering if it was taking so long due to a flaw in my code or if not im thinking hardware (Hosting it myself on a 2006 Dell with a sempron). If anyone sees anything wrong with it please let me know thanks.


Solution

  • Three problems I see with your code:

    • No sleep
    • No ob_flush
    • Sessions

    Your while() loop is constantly reading the file system. You need to slow it down. I've put a half second sleep in the below; experiment with the largest value for acceptable latency.

    PHP has its own output buffers. You use @ob_flush() to flush them (the @ suppresses errors) and flush() to flush the Apache buffers. Both are needed, and the order is important, too.

    Finally, PHP sessions lock, so if your clients might be sending session cookies, even if your SSE script does not use the session data, you must close the session before entering the infinite loop.

    I've added all three of those changes to your code, below.

    <?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);
        }
    

    BTW, the advice in the other answer about using an in-memory database is good, but the file system overhead is in milliseconds, so it won't explain a "30 second to 10 minute" latency.