来自 PHP 的 SSE 回显以随机间隔重复
SSE echo from PHP duplicating at random intervals
我正在尝试设置一个 PHP 服务器发送事件,它工作正常。但是在随机的时间间隔内,它会重复推送相同的数据。
这里有一个快速场景来阐明我所描述的内容:假设我在 1:00:00 处插入了一条数据库记录。记录的数据按原样推送。但是,在 1:03:00 处,该记录的数据被第二次推送。然后在 1:03:17 处再次被推送。我现在显示了 3 个记录实例。
为什么会发生这种情况,为什么会随机发生?
我增加了 php 执行时间,但问题仍然存在。
在浏览器控制台中,我收到此错误:net::ERR_INCOMPLETE_CHUNKED_ENCODING。
我在客户端有这个:
<script>
var source = new EventSource('pdo_updates.php');
var pdo_updates;
source.onmessage = function(e) {
pdo_updates = e.lastEventId + '' + e.data + '<br>';
document.getElementById("videoID").innerHTML += pdo_updates;
};
evtSource.close();
</script>
这是服务器端的:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0');
function send_msg($id, $msg) {
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$last_event_id = floatval(isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? $_SERVER["HTTP_LAST_EVENT_ID"] : False);
if ($last_event_id == 0) {
$last_event_id = floatval(isset($_GET["lastEventId"]) ? $_GET["lastEventId"] : False);
}
$last_id = 0;
try {
$conn = new PDO('mysql:host=localhost;dbname=my_db', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
while(1) {
$id = $last_event_id != False ? $last_event_id : $last_id;
$stmt = $conn->prepare("SELECT id, message FROM messages WHERE id > :id ORDER BY id DESC LIMIT 1");
$result = $stmt->execute(array('id' => $id));
$stmt->bindValue('id', $id);
if ($result) {
while($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
if ($data) {
send_msg($data['id'], $data['message']);
$last_id = $data['id'];
}
}
}
sleep(1);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
因此,在尝试了几乎所有与 keepalive 相关的 header 和每个 apache 超时调整配置之后,我最终 运行 捕获了一个数据包。我发现远程端正在触发 TCP 重置。我的网站支持 Cloudflare,一旦我禁用 Cloudflare,问题 部分 就消失了。 TCP session 每 100 秒刷新一次,这会导致最后一条消息再次出现。这是导致浏览器控制台错误的原因:net::ERR_INCOMPLETE_CHUNKED_ENCODING
然而,与此同时,我的提交代码出现了问题。但我不是 100% 确定为什么。
$.ajax({
type: "POST",
url: url,
data: $("#submitmessage").serialize(), // serializes the form's elements.
success: function(data)
{
$("#myvideo").val("");
}
});
提交后我需要清除输入字段中的值。从那以后我没有任何重复。
我正在尝试设置一个 PHP 服务器发送事件,它工作正常。但是在随机的时间间隔内,它会重复推送相同的数据。
这里有一个快速场景来阐明我所描述的内容:假设我在 1:00:00 处插入了一条数据库记录。记录的数据按原样推送。但是,在 1:03:00 处,该记录的数据被第二次推送。然后在 1:03:17 处再次被推送。我现在显示了 3 个记录实例。
为什么会发生这种情况,为什么会随机发生?
我增加了 php 执行时间,但问题仍然存在。
在浏览器控制台中,我收到此错误:net::ERR_INCOMPLETE_CHUNKED_ENCODING。
我在客户端有这个:
<script>
var source = new EventSource('pdo_updates.php');
var pdo_updates;
source.onmessage = function(e) {
pdo_updates = e.lastEventId + '' + e.data + '<br>';
document.getElementById("videoID").innerHTML += pdo_updates;
};
evtSource.close();
</script>
这是服务器端的:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0');
function send_msg($id, $msg) {
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$last_event_id = floatval(isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? $_SERVER["HTTP_LAST_EVENT_ID"] : False);
if ($last_event_id == 0) {
$last_event_id = floatval(isset($_GET["lastEventId"]) ? $_GET["lastEventId"] : False);
}
$last_id = 0;
try {
$conn = new PDO('mysql:host=localhost;dbname=my_db', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
while(1) {
$id = $last_event_id != False ? $last_event_id : $last_id;
$stmt = $conn->prepare("SELECT id, message FROM messages WHERE id > :id ORDER BY id DESC LIMIT 1");
$result = $stmt->execute(array('id' => $id));
$stmt->bindValue('id', $id);
if ($result) {
while($data = $stmt->fetch(PDO::FETCH_ASSOC)) {
if ($data) {
send_msg($data['id'], $data['message']);
$last_id = $data['id'];
}
}
}
sleep(1);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
因此,在尝试了几乎所有与 keepalive 相关的 header 和每个 apache 超时调整配置之后,我最终 运行 捕获了一个数据包。我发现远程端正在触发 TCP 重置。我的网站支持 Cloudflare,一旦我禁用 Cloudflare,问题 部分 就消失了。 TCP session 每 100 秒刷新一次,这会导致最后一条消息再次出现。这是导致浏览器控制台错误的原因:net::ERR_INCOMPLETE_CHUNKED_ENCODING
然而,与此同时,我的提交代码出现了问题。但我不是 100% 确定为什么。
$.ajax({
type: "POST",
url: url,
data: $("#submitmessage").serialize(), // serializes the form's elements.
success: function(data)
{
$("#myvideo").val("");
}
});
提交后我需要清除输入字段中的值。从那以后我没有任何重复。