Server-Sent Events 连接总是自动断开怎么办?
我在用 Server-Sent Events 做一个实时通知功能,后端是 PHP 写的。前端刚连上能收到几条消息,但过几秒就自动断开了,浏览器 Network 面板里看到状态变成 canceled。我查了下资料说 SSE 应该保持长连接,但不知道哪里出问题了。
后端我加了 header('Content-Type: text/event-stream'); 和 header('Cache-Control: no-cache');,还用了 flush(),但还是断。是不是我漏了什么设置?
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
while (true) {
echo "data: " . json_encode(['time' => date('Y-m-d H:i:s')]) . "nn";
flush();
ob_flush();
sleep(2);
}
1. PHP Session 锁住你了
如果你的网站在连 SSE 之前开过 session,比如用户登录什么之类的,session 会一直锁着,SSE 的长连接请求会被卡住直到超时。解决办法就是在 SSE 脚本里最开头写:
session_write_close();
2. Web 服务器的超时设置
Nginx 默认 60 秒就给你断了,不管你有没有数据。Apache 也一样。你需要在后端设置更长的超时时间:
set_time_limit(0);
ignore_user_abort(true);
还有 Nginx 那边的配置需要改:
proxy_read_timeout 300s;
proxy_send_timeout 300s;
或者直接用 Swoole 什么的长连接方案,但那是后话了。
3. 你的 flush 写法有点问题
ob_flush() 和 flush() 顺序反了,而且最好用 ob_implicit_flush(1) 代替手动 flush:
session_write_close();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // 禁用 Nginx 缓冲
ob_implicit_flush(1);
ob_end_clean();
while (true) {
echo "data: " . json_encode(['time' => date('Y-m-d H:i:s')]) . "nn";
if (connection_aborted()) {
break;
}
sleep(2);
}
4. 还有一个坑
如果你的 PHP 输出被 Gzip 压缩了,flush 是没用的。确保没开 Gzip,或者在 .htaccess/nginx 配置里对 /sse 这个路由关掉压缩。
你先试试把 session_write_close() 加上,十有八九是这个问题。