SSE加载大量数据并使AJAX post请求瘫痪

4

This is my sse_server.php file

include_once 'server_files/init2.php'; //this file includes the connection file to the database and some functions

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$assocArray = array();

$fetch_article = $dbh->prepare("SELECT 
                                        article_author_id, 
                                        article_author_un,
                                        article_id, 
                                        article_cover,
                                        article_title,
                                        article_desc
                                    FROM articles ORDER BY article_id DESC");
$fetch_article->execute();

while ($fetch = $fetch_article->fetch(PDO::FETCH_ASSOC)) 
{
    $article_author_id    = $fetch['article_author_id'];
    $article_author_u     = $fetch['article_author_un'];
    $article_id           = $fetch['article_id']; 
    $article_cover        = $fetch['article_cover'];
    $article_title        = $fetch['article_title'];
    $article_desc         = $fetch['article_desc'];


    $randomNum = rand(0,500);

    //Insert the Random Number along with the article's info | Random Number as a Value and the contents as a Key
                $assocArray[
                            'randomNum'.'|'.                    //0
                            $article_author_id.'|'.             //1
                            $article_author_u.'|'.              //2
                            $article_id.'|'.                    //3
                            $article_cover.'|'.                 //4
                            $article_title.'|'.                 //5
                            $article_desc                       //6

                            ]  = $randomNum;
}

//sort the array
arsort($assocArray, 1); 
//echo '<pre>';
//print_r($assocArray);

//while(true){

    $var = '';
    foreach ($assocArray as $key => $value) {

        $var .=  $value .' => ' . $key . '`|||`<br>';
    }
    echo "retry: 6000\n";
    echo "data: {$var}\n\n";
    ob_flush();
    flush();

//}

and this is how I'm processing the data in client.php file

    <div id="feeds"></div>
    <script>

        if(typeof(EventSource)!=="undefined") {

            var eSource = new EventSource("sse_server.php");


            //detect message received
            eSource.addEventListener('message', function(event) {


            var jsV_feeds = event.data;
            var eventList = document.getElementById("feeds");


            var jsV_feedsArray = jsV_feeds.split('`|||`');  //Seperator



            eventList.innerHTML = jsF_ToFetch(jsV_feedsArray); 

            }, false);
        }
        else {
            document.getElementById("feeds").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
        }


        function jsF_ToFetch(jsP_array) 
        {
            var string = ''; //an empty string 
            for (var i = 0; i < jsP_array.length-1; i++) 
            {

                jsV_Feed = jsP_array[i].split('|');

                jsV_randomNum           = jsV_Feed[0];
                jsV_article_author_id   = jsV_Feed[1];
                jsV_article_author_u    = jsV_Feed[2];
                jsV_article_id          = jsV_Feed[3];
                jsV_article_cover       = jsV_Feed[4];
                jsV_article_title       = jsV_Feed[5];
                jsV_article_desc        = jsV_Feed[6];


                string += jsV_randomNum +'<li><b>'+jsV_article_author_u+'</b><!--process the rest in a similar way--> </li>';


            } // for loop ENDS here
            return '<ol>' + string + '</ol>';
        }

    </script>

The Problem is if I use the foreach loop only, it reconnects every 6 seconds.
And if I wrap the foreach inside a while loop it keeps the connection alive but continously keeps on sending data. This eventually loads up a lot of data within seconds. Also it makes AJAX Post request very slow which is executed via another page simultaneously.

Why is that happening ?
How can I get it to keep the connection open, not send data, and not slow down the AJAX post requests.

PS: I have visited these links -
http://www.html5rocks.com/en/tutorials/eventsource/basics/
PHP Event Source keeps executing

May be I didn't understood them very well enough. If it could be boiled down to even simpler terms, kindly do it!

Thanks in advance!

1个回答

1
你需要使用在sse_server.php中已注释掉的while(true){}循环。你的SSE脚本不应该退出(除非socket被关闭,这将来自客户端,即你的JavaScript脚本关闭它,或浏览器窗口被关闭)。
当使用while循环时出现问题的原因是while循环内没有sleep()或等待操作。因此,你正在以最大速率向客户端发送数据(重复相同的数据!)。
从概念上讲,我猜你想要的代码是这样的:
$lastID = 0;
while(true){
  $fetch_article = $dbh->prepare("SELECT something FROM somewhere WHERE conditions AND articleID > ?");
  $results = $fetch_article->execute($lastID);
  if(has 1+ results) {
     foreach($results){
        echo result formatted for SSE
        $lastID = ...;
     }
     flush();ob_flush();
  }
  sleep(5);
}

这段话的意思是,它会每5秒轮询一次数据库以获取记录。如果没有新记录,它就什么也不做 - 只是再次进入睡眠状态等待另外5秒。但如果有新记录,则通过SSE将它们推送给客户端。
你可以调整5秒的休眠时间来找到服务器CPU使用和延迟之间的平衡点。更短的休眠时间意味着延迟更低(您的客户端更快地获取新数据),但服务器上的CPU使用率更高。
附注:上面的lastID方法只是一种检测您已经看到哪些记录,还有哪些记录尚未看到的方法。如果您的表中有一个唯一的ID,该ID是AUTO_INCREMENT的,则这种方法非常好。但是,如果DB记录是带有created时间戳插入的,则查询变为:
$now = 0;
while(true){
    $stmt = prepare( "SELECT ... AND created > ?" );
    $stmt->execute( $now );
    $now = time();
    ...process results ...
    sleep(5);
}

较为安全的做法是将$now设置为结果中找到的最大created时间戳,而不是每次都设置为time(); 否则可能会有记录被遗漏,并未发送给客户端。


使用while循环可以保持连接活动状态,但不会发送更多数据,但它会减慢从另一页执行的ajax post请求。它还会减慢页面加载时间。 那么我是否必须在while循环中运行SQL查询和所有用于排序结果的处理,或者在其外部运行是否可以? - Devang Mistry
@Devang,你为什么要使用SSE?是为了不断向客户端推送来自数据库的最新数据吗?如果是这样,那么数据库调用必须在while循环内部。如果你只想获取一次数据库的快照,那么你应该使用简单的ajax轮询请求,而不是SSE。 - Darren Cook
@Devang 关于它减缓其他连接的问题;除非您的Web服务器(或防火墙等)设置为每个IP地址保持打开的套接字数量非常低,否则不应该发生这种情况。也就是说,如果您已经实施了我建议的更改,则该套接字每5秒要么根本不发送任何内容,要么每5秒发送一小段数据。两者都不应该足以干扰其他套接字的带宽。(我假设名为“articles”的表不会每5秒钟获得数百万甚至数百条新记录!) - Darren Cook
是的,它是持续地从数据库中推送最新数据给客户端!就像 Twitter 一样。我现在正在使用本地主机,所以这可能是因为它吗? 而且,我可以在一个页面中使用多个 EventSource 对象吗? - Devang Mistry
我正在按照你的建议进行操作,保持连接开放并仅发送一次数据。我将在其中包含SQL查询。$lastID = 0; $Length = count($assocArray); while(true) { $var = ''; foreach ($assocArray as $key => $value) { if($lastID <= $Length){ $k = explode('|', $key); $var = $value .' => '.$lastID. '-' . $k[3] .' '.$k[5]. '`|||`
'; echo "retry: 6000\n"; echo "data: {$var}\n\n"; $lastID++; ob_flush(); flush(); } $var = ''; } sleep(5); }
- Devang Mistry
@Devang 评论中的代码可能有点难以阅读,但我关心比较$lastID和$Length。我已经编辑了我的答案来解释最后一个ID位是用来做什么的。 - Darren Cook

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