FrankenPHP的Mercure不支持Last-Event-ID,因其仅转发事件而不存储ID或提供重放;恢复断连消息需依赖业务层状态管理,如前端传resume参数、后端按状态过滤推送。

FrankenPHP 的 Mercure 不支持 Last-Event-ID
FrankenPHP 内置的 Mercure Hub 严格遵循 text/event-stream 协议语义,但**不解析或存储 Last-Event-ID 请求头**。它只做发布-订阅转发,不维护事件序列号、不提供重放窗口、也不记录客户端消费进度。你看到的 Last-Event-ID 头在 Mercure 场景下是无效的——浏览器发了,Hub 收了,但直接忽略。
想恢复断连后的消息?必须靠服务端状态 + 客户端重订
真正能恢复的不是“从某 ID 继续”,而是“从某个业务状态重新订阅”。比如订单进度推送,恢复逻辑应基于:
- 前端保存上次收到的
progress值(如65),刷新后发起新订阅时带上?resume=65查询参数 - 后端 PHP 脚本(如
/stream-order-status.php)读取该参数,跳过已知状态,只推送后续变更 - 若用 Mercure,则需在发布前检查当前状态是否已高于该值,避免重复广播
注意:frankenphp\publish() 不提供“按 ID 补推”能力,它只负责把数据发给 Hub;补推逻辑必须由你自己的业务代码控制。
EventSource 断连后自动重连,但不会带 Last-Event-ID
浏览器原生 EventSource 在连接断开后会自动重试,但它默认发送的请求不含 Last-Event-ID 头——除非你手动配置:
立即学习“PHP免费学习笔记(深入)”;
- 创建实例时传入
withCredentials: true并确保服务端允许 - 在
onerror回调里记录最后收到的事件 ID(从e.lastEventId读取) - 重连时用新 URL 拼接
&last_event_id=123,例如:new EventSource('/mercure?topic=order/123&last_event_id=42') - 后端脚本需显式读取
$_GET['last_event_id']并据此过滤数据源
这个方案可行,但和 Mercure 无关,本质是自建 SSE 流控,适用于你完全掌控流端点(如 /stream-order-status.php)的场景。
别混淆 Mercure 和纯 SSE:协议层没有重放能力
Mercure 是基于 SSE 的发布-订阅协议扩展,但它没实现 RFC 中可选的事件 ID 存储与回溯机制。它的设计目标是低延迟广播,不是消息队列。所以:
-
curl -H 'Last-Event-ID: 100' http://localhost/.well-known/mercure?topic=xxx不会返回从 100 开始的事件 - Caddy 的 Mercure 模块不暴露
/mercure/events?since=...这类端点 - 若需要强顺序+重放,得引入 Redis Stream 或 PostgreSQL LISTEN/NOTIFY,再由 Worker 主动拉取并按序推送给 Mercure
最容易被忽略的一点:你以为浏览器重连会“接着上次播”,其实它只是重新打开一个全新订阅通道——所有状态同步责任都在你手上,Mercure 只管“现在发什么”。



















