应通过浏览器network面板看text/event-stream请求是否pending且持续追加data行、服务端日志是否只打一次连接标记、access.log中duration是否远超普通接口,并确认php未exit、已调用flush()、禁用output_buffering、opcache及时验证及caddy timeout_idle足够长。

怎么确认浏览器还在连着 FrankenPHP 的 SSE 端点
FrankenPHP 本身不暴露连接状态面板,但你可以从浏览器、服务端日志、HTTP 连接三处交叉验证是否真“连着”。关键不是看 EventSource 实例有没有报错,而是看底层 TCP 连接是否活跃、服务端是否持续写入流。
浏览器 DevTools 里怎么看连接还活着
打开 Chrome / Edge 的 Network 面板,筛选 text/event-stream 类型请求:
- 状态码是
200且 Status 显示(pending)或持续有“时间戳递增”(如 1.2s → 2.8s → 4.1s),说明连接未断、服务端仍在响应 - Response 标签页里能看到不断追加的
data:行(哪怕内容为空),代表流在持续推送 - Headers → Response Headers 中必须包含
Content-Type: text/event-stream和Cache-Control: no-cache,缺一不可;否则 EventSource 会直接失败或静默关闭 - 如果看到状态变成
cancelled或反复出现新请求(同一 URL 多个 pending 条目),大概率是服务端提前 close()、超时中断,或客户端触发了重连逻辑
FrankenPHP 服务端怎么判断连接没丢
FrankenPHP 没有内置连接池监控,但你可以靠日志 + PHP 进程行为间接确认:
- 在 SSE 路由处理逻辑中,用
error_log("SSE: client connected, pid=".getmypid());打点,每次新连接都输出一次;若只打一次、后续无新日志,说明连接复用成功 - 检查
access.log(如果 Caddy 启用了):正常长连接下,该请求的duration字段会远大于普通接口(比如 >60s),且不会频繁新建条目 - 注意:FrankenPHP 的 PHP worker 是常驻的,
fclose($stdout)或脚本结束会立即断开流;必须用flush()+ob_flush()持续输出,且不能 exit/return,否则连接立刻终结 - 若你用了
ServerSentEvents类(如 Webman 场景),它内部会检测客户端是否断开,但 FrankenPHP 原生模式下需自己轮询connection_aborted()
为什么 EventSource 显示 onopen 却收不到 data
常见于 FrankenPHP 配置或 PHP 输出控制不当,不是连接问题,而是流被截断或阻塞:
-
opcache.enable=1且opcache.validate_timestamps=0(CLI 模式默认如此)会导致新改的 SSE 控制器代码不生效,看似连接成功,实则执行的是旧逻辑 - PHP 缓冲区未关:必须在输出前调用
ob_end_flush()、禁用output_buffering,否则数据卡在缓冲区不发出去 - Caddy 层超时:Caddy 默认
timeout_idle是 5m,若你的 SSE 不发心跳(空:注释行),连接可能被中间代理或负载均衡器主动 kill - FrankenPHP 的
startup配置为0时,worker 可能被回收,导致正在跑的 SSE 流突然终止——应设为1或更高值保证常驻
最易忽略的一点:FrankenPHP 下的 SSE 必须全程由同一个 PHP worker 处理,而 worker 不自动感知文件变更。哪怕你改了控制器、加了 sleep(1)、再 reload Caddy,旧 worker 仍跑着老字节码——得手动 kill -TERM $PID 或启用 watch: true 触发重启,否则永远收不到新 data。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











