导读:本期聚焦于吴凌云创作的《php实时输出配合sse怎么做?SSE服务器推送实战教程》,敬请观看详情。想让页面在不刷新的情况下持续收到服务器发来的数据,SSE(Server-Sent Events)是最轻量的方案之一。本文围绕PHP如何实现SSE实时推送展开,先讲清楚SSE的工作机制和报文格式,再给出PHP端循环输出、连接保活、断线重连的完整代码,同时对比flush失效、缓冲区不刷新、Nginx代理缓冲导致消息卡住等常见坑,并附上前端EventSource的接收写法。如果你正在做日志实时查看、进度条推送或消息通知这类功能,这篇教程可以直接拿来参考。

SSE(Server-Sent Events)是一种基于HTTP长连接的服务器推送技术,浏览器只发一次请求,服务器就能持续不断地往客户端写数据,直到连接关闭。相比WebSocket,SSE不需要额外的协议升级,纯HTTP就能跑通,而且自带断线重连机制,对于只需要服务器单向推送数据的场景(比如日志监控、任务进度、消息通知),用它比WebSocket省事得多。PHP虽然不是常驻内存型语言,但借助输出缓冲的实时刷新,照样可以把SSE做得稳定可用。

php实时输出配合sse怎么做?SSE服务器推送实战教程

一、先搞懂SSE的报文格式,这是实现的基础

SSE本质上就是一个永不结束的HTTP响应流,服务器不断往响应体里写符合特定格式的文本。响应头必须包含三个关键部分:Content-Type: text/event-stream告诉浏览器这是一个事件流,Cache-Control: no-cache禁止缓存,Connection: keep-alive保持长连接(HTTP/1.1下可省略)。

数据格式方面,每条消息由若干字段组成,最常用的是data:字段,后面跟消息内容,消息之间用两个换行符\n\n分隔。比如推送一条文本消息就是这样:

echo "data: 这是一条推送消息\n\n";

除了data:,还有几个字段需要了解。event:可以自定义事件类型,前端监听时可以只监听特定事件;id:用于给消息编号,配合浏览器的Last-Event-ID请求头实现断线续传;retry:指定断线后浏览器自动重连的等待毫秒数。这些字段可以组合使用,一条完整消息的写法如下:

echo "id: 1001\n";
echo "event: progress\n";
echo "retry: 3000\n";
echo "data: {\"percent\": 45}\n\n";

注意data的内容如果有多行,每一行前面都要加data:前缀,浏览器会自动用换行拼接。JSON里的双引号不需要额外转义成实体,因为这是纯文本流,不经过HTML解析。

二、PHP端实现实时输出,关键是冲掉所有缓冲区

PHP默认开启了输出缓冲,echo的内容会先停留在PHP进程缓冲区、再到Web服务器的缓冲区,缓冲满了才真正发给浏览器。这对SSE是致命的,消息会堆积在那里迟迟不出。所以服务端脚本要做两件事:关闭PHP自身的输出缓冲,并在每次echo后强制刷新。

下面是一个完整可运行的服务端示例,模拟每秒推送一次进度,推完自动结束:

<?php
// 关闭脚本执行时间限制,长连接必须
set_time_limit(0);

// 关键响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // 告诉Nginx不要缓冲响应

// 关闭压缩,gzip会让flush失效
@ini_set('zlib.output_compression', 'off');
@ini_set('output_buffering', 'off');

// 冲掉PHP层面可能存在的缓冲区
while (ob_get_level() > 0) {
    ob_end_flush();
}

for ($i = 1; $i <= 10; $i++) {
    echo "id: {$i}\n";
    echo "data: " . json_encode(['step' => $i, 'msg' => "正在处理第 {$i} 步"], JSON_UNESCAPED_UNICODE) . "\n\n";
    // 立即发送给客户端
    flush();
    sleep(1);
}

echo "event: done\ndata: 任务完成\n\n";
flush();

代码里有几个细节值得展开。第一,set_time_limit(0)不可省略,否则默认30秒后PHP会杀掉脚本,连接直接断掉。第二,while (ob_get_level() > 0) ob_end_flush()这一段是为了清干净所有嵌套的输出缓冲,有些框架(比如Laravel)会在外层包好几层缓冲区,不清理的话flush刷的只是最内层,消息照样出不去。第三,X-Accel-Buffering: no这个头专门给Nginx看,因为Nginx默认会缓冲后端响应,攒够一定量才转发给客户端,加上这个头才能实现逐条送达。

如果是长驻型推送(不是推完就结束),建议在循环里加上心跳机制:每隔15到25秒发一条注释行: ping\n\n。SSE规范允许以冒号开头的行作为注释,浏览器会忽略它,但数据能起到保活作用,防止中间的代理或防火墙因为长时间无数据而掐断连接。

三、前端用EventSource接收,附断线重连处理

浏览器原生提供了EventSource对象,几行代码就能建立连接并接收消息,不需要引入任何库:

const es = new EventSource('/sse.php');

// 监听默认的message事件
es.onmessage = function (e) {
    const data = JSON.parse(e.data);
    console.log('收到推送:', data.msg);
};

// 监听自定义的done事件,收到后主动关闭
es.addEventListener('done', function (e) {
    console.log('服务端通知:', e.data);
    es.close();
};

// 出错时浏览器会按retry指定的间隔自动重连
es.onerror = function () {
    console.log('连接异常,等待自动重连');
};

这里有个容易踩的坑:当服务端脚本正常结束后,连接会关闭,EventSource会认为是异常断开,自动发起重连,导致服务端脚本被反复执行。解决办法有两种:一是前端在收到结束事件后主动调用es.close(),就像上面代码里做的那样;二是服务端发送一个自定义事件标记结束,前端据此关闭。二者选其一即可。

另外,EventSource只支持GET请求,也无法自定义请求头。如果需要传鉴权信息,可以把token拼在URL参数里,或者改用fetch配合ReadableStream手动解析流,后者能发POST请求,代价是要自己处理重连和消息分帧。

四、常见问题排查:消息卡住不出怎么办

实际部署时最常见的问题就是本地好好的,一上服务器消息就成批到达或者压根不出,基本都是缓冲环节出了问题。可以从下面几个方向依次排查。

第一层是PHP配置。检查php.ini里的output_buffering是否为Offzlib.output_compression是否关闭。如果用的是php-fpm,还要留意fastcgi的缓冲设置。

第二层是Web服务器。Nginx需要在站点配置里对SSE路径做特殊处理,除了前面说的响应头,也可以直接在配置里写明:

location /sse.php {
    proxy_pass http://127.0.0.1:9000;
    proxy_buffering off;      # 关闭代理缓冲
    proxy_cache off;          # 关闭缓存
    proxy_read_timeout 3600s; # 延长读取超时,防止长连接被断
    proxy_set_header Connection '';
    proxy_http_version 1.1;
}

第三层是浏览器侧的确认。Chrome开发者工具的Network面板里,SSE请求会显示为pending状态,切到EventStream标签页可以看到每条到达的消息。如果那里有消息而页面没反应,问题在前端代码;如果那里也没消息,问题在服务端或中间层。

最后提一下连接数的问题。HTTP/1.1下浏览器对同一域名只允许6个并发连接,SSE会长期占用其中一个,如果页面上开多个SSE连接可能把配额耗尽,导致其他请求排队。解决办法是走HTTP/2(多路复用没有这个限制),或者合并多个推送通道到一个SSE连接里,用event字段区分消息类型。

总结一下,PHP实现SSE推送的核心就三点:正确的响应头、逐条echo加flush、清理各级缓冲。把这几步做扎实,再处理好心跳保活和断线关闭,日志实时滚动、任务进度条这类需求就能稳定落地了。

php SSE实时输出服务器推送修改时间:2026-09-03 15:39:13

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49652.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。