PHP实现Server-Sent Events时readyState始终为0及批量事件问题排查
关于Server-Sent Events(SSE)的两个问题
问题1:客户端连接时readyState始终为0
我正在使用EventSource(SSE API)实现Server-Sent Events,按照示例搭建Demo,但客户端连接时readyState始终处于0状态;移除PHP文件中的while循环后,连接能正常工作一次但随后中断。
相关代码
PHP代码:
<?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); while(true) { $time = date('r'); echo "data: The server time is: {$time} "; ob_end_flush(); flush(); session_write_close(); if(connection_aborted()) { break; } sleep(1); }
JavaScript代码:
var evtSource = new EventSource('/pingen/PingenEventHandler.php'); console.log(evtSource.withCredentials); console.log(evtSource.readyState); console.log(evtSource.url); evtSource.onopen = function() { console.log("Connection to server opened."); }; evtSource.onmessage = function(e) { console.log(e.data) }; evtSource.onerror = function(event) { console.log(event); console.log("EventSource failed."); };
问题2:事件批量到达而非逐个发送
更新PHP代码后,客户端可以接收事件,但事件并非逐个发送,而是约20个一起到达。
更新后的PHP代码:
header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); while(true) { $data = [ 'name' => 'Rasmus Lerdorf' ]; $curDate = date(DATE_ISO8601); echo "event: sse "; echo "data: This is a message at time " . $curDate . " "; echo str_pad('', 4096) . " "; ob_end_flush(); flush(); session_write_close(); if(connection_aborted()) { break; } sleep(1); }
问题1的可能原因及解决方案
- 输出缓冲未完全关闭:PHP的输出缓冲或服务器端(Apache、Nginx)的额外缓冲会阻塞内容实时发送。可在脚本开头添加
ob_start()手动控制缓冲;检查服务器配置,比如关闭Apache的mod_deflate、Nginx的gzip对SSE接口的压缩。 - 会话锁定问题:若脚本执行前已开启会话,
session_write_close()的调用时机可能太晚。建议在输出任何内容前就调用该函数,或直接在SSE接口中禁用会话(无需会话时)。 - 服务器长连接限制:Apache的
Timeout、Nginx的proxy_read_timeout等配置可能提前断开长连接,导致客户端停留在连接中状态。需调整超时时间至大于SSE发送间隔。 - 跨域限制:若前端与SSE接口不同域名,需在PHP中添加跨域头,比如
header("Access-Control-Allow-Origin: 你的前端域名");,否则浏览器会阻止连接建立。
问题2的可能原因及解决方案
事件批量到达核心是输出缓冲未即时刷新,常见原因及解决:
- 服务器端缓冲阈值过高:你添加的4096字节填充可能未达到服务器缓冲阈值(比如Nginx默认8KB),可增大填充至
str_pad('', 8192);或在服务器配置中禁用该接口缓冲:Nginx添加fastcgi_buffering off;,Apache设置SetEnv no-gzip 1。 - PHP自身缓冲未关闭:检查php.ini中
output_buffering是否为Off,或在脚本开头用ini_set('output_buffering', 0);强制关闭。 - 网络代理/CDN缓冲:若存在代理或CDN,需配置其不对SSE接口的响应进行缓冲,或直接绕过缓存访问该接口。
内容的提问来源于stack exchange,提问作者Mischa Binder
相关产品推荐
相关产品推荐

