SSE 加载大量数据并瘫痪 AJAX post 请求
SSE loads lots of data and paralyzes AJAX post requests
这是我的sse_server.php文件
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();
//}
这就是我处理 client.php 文件
中数据的方式
<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>
问题是如果我只使用 foreach
循环,它每 6 秒重新连接一次。
如果我将 foreach
包装在 while
循环中,它会保持连接处于活动状态,但会持续 继续发送数据 。这最终会在几秒钟内加载大量数据。它还使 AJAX Post 请求非常慢,同时通过另一个页面执行。
为什么会这样?
我怎样才能让它保持连接打开,不发送数据,并且不减慢 AJAX post 请求。
PS: 我访问过这些链接 -
http://www.html5rocks.com/en/tutorials/eventsource/basics/
PHP Event Source keeps executing
可能是我理解的不够深入。如果可以将其归结为更简单的术语,请这样做!
提前致谢!
您想使用在 sse_server.php 中注释掉的 while(true){}
循环。您的 SSE 脚本永远不应该退出(直到套接字关闭,这将在客户端发生,即您的 JavaScript 脚本关闭它,或者浏览器 window 被关闭)。
您在使用 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 秒轮询一次数据库以获取 new 记录。如果没有新记录,它什么都不做——只是再睡 5 秒。但是,如果有新记录,它会将它们通过 SSE 推送到客户端。
您可以调整 5 秒睡眠以在 CPU 服务器使用和延迟之间找到平衡。更短的睡眠意味着更低的延迟(您的客户端更快地获得新数据),但在服务器上更高 CPU。
旁白: 上面的 lastID
方法只是一种检测您已经看到和还没有看到的记录的方法。如果您的 table 中有一个唯一 ID 就好了,即 AUTO_INCREMENT
。但是,或者,如果插入的数据库记录带有 created
时间戳,则查询变为:
$now = 0;
while(true){
$stmt = prepare( "SELECT ... AND created > ?" );
$stmt->execute( $now );
$now = time();
...process results ...
sleep(5);
}
(稍微安全一点的是将 $now
设置为在结果中找到的最大 created
时间戳,而不是每次都设置为 time()
;否则记录可能从裂缝中溜走,不会发送给客户。)
这是我的sse_server.php文件
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();
//}
这就是我处理 client.php 文件
中数据的方式 <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>
问题是如果我只使用 foreach
循环,它每 6 秒重新连接一次。
如果我将 foreach
包装在 while
循环中,它会保持连接处于活动状态,但会持续 继续发送数据 。这最终会在几秒钟内加载大量数据。它还使 AJAX Post 请求非常慢,同时通过另一个页面执行。
为什么会这样?
我怎样才能让它保持连接打开,不发送数据,并且不减慢 AJAX post 请求。
PS: 我访问过这些链接 -
http://www.html5rocks.com/en/tutorials/eventsource/basics/
PHP Event Source keeps executing
可能是我理解的不够深入。如果可以将其归结为更简单的术语,请这样做!
提前致谢!
您想使用在 sse_server.php 中注释掉的 while(true){}
循环。您的 SSE 脚本永远不应该退出(直到套接字关闭,这将在客户端发生,即您的 JavaScript 脚本关闭它,或者浏览器 window 被关闭)。
您在使用 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 秒轮询一次数据库以获取 new 记录。如果没有新记录,它什么都不做——只是再睡 5 秒。但是,如果有新记录,它会将它们通过 SSE 推送到客户端。
您可以调整 5 秒睡眠以在 CPU 服务器使用和延迟之间找到平衡。更短的睡眠意味着更低的延迟(您的客户端更快地获得新数据),但在服务器上更高 CPU。
旁白: 上面的 lastID
方法只是一种检测您已经看到和还没有看到的记录的方法。如果您的 table 中有一个唯一 ID 就好了,即 AUTO_INCREMENT
。但是,或者,如果插入的数据库记录带有 created
时间戳,则查询变为:
$now = 0;
while(true){
$stmt = prepare( "SELECT ... AND created > ?" );
$stmt->execute( $now );
$now = time();
...process results ...
sleep(5);
}
(稍微安全一点的是将 $now
设置为在结果中找到的最大 created
时间戳,而不是每次都设置为 time()
;否则记录可能从裂缝中溜走,不会发送给客户。)