You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AJAX占用PHP输出导致Server-Sent Events推送通知失效求助

解决方案:分离AJAX与SSE的处理逻辑

问题核心在于同一个PHP脚本同时处理AJAX请求和SSE推送请求时,AJAX会捕获脚本的所有输出,包括SSE的响应内容——这两种请求的响应格式和生命周期完全不同,AJAX需要短连接的常规响应,而SSE需要长连接的text/event-stream格式输出,强行放在同一个文件里必然冲突。

最优方案:拆分请求处理文件

把AJAX数据接收和SSE推送逻辑拆成两个独立文件,从根源上隔离输出流:

  1. AJAX数据接收文件(ajax-handler.php)
    负责接收前端AJAX提交的数据,并存入存储介质(SESSION、数据库或缓存):
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 接收AJAX传递的通知内容
    $notice = $_POST['notice_content'];
    // 存入SESSION队列(多用户场景建议用数据库+用户ID标识)
    $_SESSION['pending_notices'][] = $notice;
    // 返回AJAX成功响应
    echo json_encode(['code' => 200, 'msg' => '通知已待推送']);
    exit(); // 必须终止脚本,避免多余输出
}
  1. SSE推送文件(push-notifications.php)
    专门处理EventSource的长连接请求,从存储介质读取待推送内容并输出:
<?php
session_start();
// 设置SSE响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

// 初始化通知队列
if (!isset($_SESSION['pending_notices'])) {
    $_SESSION['pending_notices'] = [];
}

// 长连接循环
while (true) {
    // 检查是否有未推送的通知
    if (!empty($_SESSION['pending_notices'])) {
        $current_notice = array_shift($_SESSION['pending_notices']);
        // 按照SSE格式输出数据(必须以\n\n结尾)
        echo "data: " . json_encode(['message' => $current_notice]) . "\n\n";
        // 强制刷新输出缓冲区
        ob_flush();
        flush();
    }
    // 休眠3秒,减少服务器资源占用
    sleep(3);
}

客户端对应代码

  • AJAX提交数据:
fetch('ajax-handler.php', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: 'notice_content=测试推送通知'
})
.then(res => res.json())
.then(data => console.log(data.msg));
  • SSE监听推送:
const eventSource = new EventSource('push-notifications.php');
eventSource.onmessage = function(event) {
    const data = JSON.parse(event.data);
    console.log('收到推送:', data.message);
    // 这里处理前端通知展示逻辑
};

应急方案:同一文件内严格隔离请求逻辑

如果无法拆分文件,必须在脚本开头就判断请求类型,处理完AJAX后立即终止脚本,绝对不让SSE代码执行:

<?php
session_start();

// 优先处理AJAX请求(通过请求头判断)
if ($_SERVER['REQUEST_METHOD'] === 'POST' 
    && isset($_SERVER['HTTP_X_REQUESTED_WITH']) 
    && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
    $notice = $_POST['notice_content'];
    $_SESSION['pending_notices'][] = $notice;
    echo json_encode(['code' => 200]);
    exit(); // 关键:终止脚本,SSE代码不会被执行
}

// 处理SSE请求(只有非AJAX请求才会走到这里)
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

// 后续SSE循环逻辑同上文
while (true) {
    // ...推送逻辑
    sleep(3);
}

为什么之前的方法无效?

你之前尝试的ob_flush()/flush()和SESSION循环方案,本质上没有解决输出流被AJAX捕获的核心问题——只要AJAX请求触发了脚本执行,脚本的所有输出(包括SSE部分的内容)都会被AJAX的响应流接收,而AJAX无法解析SSE格式的输出,自然无法正常传递给EventSource。只有让两种请求的处理完全隔离,才能避免输出冲突。

内容的提问来源于stack exchange,提问作者Andrews D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:07:53