PHP Server-Sent Events导致其他请求阻塞问题求助
SSE连接导致其他请求阻塞的问题排查与解决
问题描述
我在PHP服务器上实现了一个简单的SSE端点,在Angular应用中通过Azure Event Source订阅它,以此在请求中发送自定义请求头。连接本身可正常工作,也能收到流数据,但一旦建立SSE连接,其他所有请求都会被阻塞,处于“Pending”状态,直到关闭SSE端点的连接。我已尝试使用session_write_close();但无济于事,希望找到不影响其他请求的解决方案。
PHP服务端实现
public function newSSEEvent() { session_write_close(); header("Content-Type: text/event-stream"); header("Cache-Control: no-store"); $counter = rand(1, 10); while (true) { echo "event: Date\n"; $curDate = date(DATE_ISO8601); echo 'data: {"time": "' . $curDate . '"}'; echo "\n\n"; $counter--; if (!$counter) { echo "event: Ping\n"; echo 'data: Last Ping' . $curDate . "\n\n"; $counter = rand(1, 10); } ob_flush(); flush(); sleep(1); } }
Angular客户端实现
async getEvent(url) { await fetchEventSource( url, { method: "GET", headers: { // 自定义请求头 }, onmessage(ev) { console.log(ev); }, } ); }
解决方案建议
1. 确保Session完全关闭
虽然调用了session_write_close(),但要确认它在任何可能触发Session启动的代码之后执行,且后续代码不再操作Session。如果使用框架(如Laravel),框架可能自动启动Session,需将session_write_close()放在输出响应头之前,确保Session锁被释放。
2. 强制清理输出缓冲区
部分服务器配置会强制开启output_buffering,导致ob_flush()和flush()无法及时输出内容。可在代码开头添加:
ob_end_clean(); // 关闭并清空所有层级的输出缓冲区
也可在php.ini中设置output_buffering = Off(需服务器权限)。
3. 优化服务端循环逻辑
PHP同步阻塞式循环会占用单个进程/线程,在Apache prefork模式下,进程数有限时会导致其他请求排队:
- 加入客户端断开检测,避免无效进程占用:
if (connection_aborted()) { break; } - 改用PHP-FPM动态进程管理,确保有足够空闲进程处理其他请求;
- 长期方案:使用Swoole/Workerman等异步框架实现SSE,从根源解决阻塞问题。
4. 规避浏览器并发限制
浏览器对同一域名的并发请求数有限(通常为6个),SSE连接会占用一个名额。可:
- 将SSE请求放在独立子域名下,绕过同一域名的并发限制;
- 客户端避免用
await阻塞主线程(当前代码的await仅影响函数本身,不影响其他请求,主要问题仍在服务端)。
内容的提问来源于stack exchange,提问作者DN0300
相关产品推荐
相关产品推荐

