AJAX占用PHP输出导致Server-Sent Events推送通知失效求助
解决方案:分离AJAX与SSE的处理逻辑
问题核心在于同一个PHP脚本同时处理AJAX请求和SSE推送请求时,AJAX会捕获脚本的所有输出,包括SSE的响应内容——这两种请求的响应格式和生命周期完全不同,AJAX需要短连接的常规响应,而SSE需要长连接的text/event-stream格式输出,强行放在同一个文件里必然冲突。
最优方案:拆分请求处理文件
把AJAX数据接收和SSE推送逻辑拆成两个独立文件,从根源上隔离输出流:
- 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(); // 必须终止脚本,避免多余输出 }
- 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
相关产品推荐
相关产品推荐

