FrankenPHP 默认实时推送使用 Mercure(SSE 协议),非 WebSocket;Mercure 基于 HTTP,客户端用 EventSource 订阅,服务端调用 $hub->publish() 即可推送,内置 Hub 无需额外部署,但需显式启用 --mercure 并配置 JWT 鉴权。

FrankenPHP 的实时推送不是 WebSocket,它默认走的是 Mercure(SSE 协议),两者协议层、连接模型、适用场景完全不同。
FrankenPHP 默认用 Mercure 而不是 WebSocket
Mercure 是基于 HTTP 的服务端推送协议,客户端用 EventSource 订阅,服务端发 text/event-stream 响应;WebSocket 则是独立 TCP 连接,需手动处理握手、帧解析、心跳。FrankenPHP 内置 Mercure Hub,启动时加 --mercure 就能直接用,不需要额外部署服务或写底层网络逻辑。
- Mercure 默认关闭,必须显式启用:
frankenphp php-server --mercure --worker ./public/worker.php - 客户端订阅地址是标准 HTTP URL:
new EventSource("http://localhost:8080/.well-known/mercure?topic=orders") - 服务端推送只需调用
$hub->publish(),FrankenPHP 已注入$hub到请求上下文 - 浏览器自动重连、天然支持 CORS、不穿透代理、无 TLS 升级烦恼
WebSocket 在 FrankenPHP 中是“手写 Worker”模式
FrankenPHP 不提供 WebSocketServer 类,也不自动解析帧数据——它只暴露 frankenphp_handle_request() 回调入口,由你读取原始请求流、计算 Sec-WebSocket-Accept、收发掩码帧。这本质是把 PHP 当成一个轻量 TCP 处理器用,而非封装好的 WebSocket 服务。
- 必须自己校验
Upgrade: websocket和Sec-WebSocket-Key - 消息体是带掩码的二进制帧,
file_get_contents('php://input')不能直接用,得解析 WebSocket 帧结构 - 没有内置广播、连接管理、心跳保活,全靠你自己用 APCu 或外部存储维护连接列表
- 每次请求仍重建 PHP 运行时(非 Swoole 那种真常驻),
static $clients = []在下一次请求中就为空
选 Mercure 还是手写 WebSocket?看这三点
不是技术越“底层”越好,而是匹配业务对连接模型的真实需求:
立即学习“PHP免费学习笔记(深入)”;
- 只需要服务端推通知(如订单更新、AI 流式输出)→ 用
Mercure:简单、可靠、省心,10 行代码就能跑通 - 需要双向实时交互(如聊天、协同编辑、游戏指令)→ 手写
WebSocketWorker:但得接受调试成本高、连接状态难维护 - 要兼容老旧环境(IE)或强依赖轮询兜底 → 两种都不适合,得回退到长轮询 + 后端队列
- Mercure 支持 JWT 鉴权、主题过滤、最后事件 ID 恢复,WebSocket 所有这些都得自己实现
最容易被忽略的一点:Mercure 的推送是“无状态”的,发完即忘;而 WebSocket 连接是“有状态”的,每个连接对应一个可写的 socket 资源——这意味着前者水平扩展容易,后者必须引入连接网关或共享存储来同步状态。



















