如何使服务器发送的事件(SSE)与PHP会话一起工作


How to make Server Sent Events (SSE) work with PHP sessions?

我使用SSE事件将用户的好友请求计数从服务器推送到客户端。但当我这样做的时候,网站的其他页面会被卡住并继续加载(可能是由于用户会话锁定)。如何将SSE事件数据推送到客户端,而不让其他页面在会话运行时等待?

我在客户端有一个事件监听器,如下所示:

var evtSource = new EventSource("friend-req.php");
var eventList = $('#list'); //this is div's ID on xhtml page
$(document).ready(function(){
	
$(function(){
	evtSource.addEventListener("requests", function(e) {
		var newElement = document.createElement("li");
			  
		var obj = JSON.parse(e.data);
		newElement.innerHTML = "friend request count: " + obj.count;
		eventList.append(newElement);
	}, false);
});
});

在服务器端,我使用了一个eventwhile循环(正如许多关于SO的文章所建议的那样)。首先,我从数据库中获取当前会话用户的好友请求计数,然后将其推送给事件"requests"(客户端正在侦听)。

<?php
//friend-req.php file
session_start();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
include_once 'db.php';
$db = new db();
while (1) {
    $user = $_SESSION['user'];
    $q = "select count(*) num from requests where status = 1 and id = $user";
    $result = $db->select($q) or die("Query Failed:" . $db->last_error);
    if($result)
    {
        $r = $db->get_row($result);
        echo "event: requests'n";
        echo 'data: {"count": "' . $r['num'] . '"}';
        echo "'n'n";
    }
    ob_flush();
    flush();
    sleep(5);
}
?>

问题是,当我运行客户端脚本时,它运行得很好,但没有加载任何其他页面。我已经搜索了解决方案,但在SO和其他网站上都没有找到任何令人满意的答案。告诉我如何通过会话发送服务器端事件,而不让其他页面等待(或者如果方法中有任何缺陷/更好的方法)

我认为问题出在"session_start()"中:每个会话都会锁定服务器中的一个文件,因此在第一个脚本结束和/或释放锁定之前,没有其他脚本可以开始使用同一个会话(它们都挂起了"session_start()"调用)。您的脚本应该读取循环外的$user,然后调用session_commit(),这样其他脚本就可以运行了。请记住,会话锁定总是阻止脚本的并行执行:如果您已经完成了对会话变量的读取或修改,请始终在脚本中调用session_commit(),这样其他脚本就可以在第一个完成详细阐述时运行(在谷歌上搜索其他更详细的解决方案)。这一点非常重要,例如,如果您的浏览器使用会话对php代码进行大量异步ajax调用:如果没有"session_commit()",您将看到您的服务器一次一个地响应请求,而没有任何并行性