概述
长期以来,Laravel 的广播层提供了 Reverb、Pusher、Ably 这类”WebSocket 原生”驱动,而现在config/broadcasting.php 的 driver 中新增了 mercure。
Mercure 是一种基于 Server-Sent Events (SSE) 的实时通信协议。它并非使用 WebSocket 那样的双向连接,而是运行在类似 HTTP/1.1 或 HTTP/2 长轮询之上,因此具有以下特点:
- 可以透明地穿越常见的 HTTP 基础设施(反向代理、CDN、负载均衡器)
- 客户端只需浏览器内置的
EventSourceAPI 即可实现,无需专门的客户端库 - FrankenPHP 内置了 Mercure hub,无需额外基础设施即可运行
新增的 driver 值
config/broadcasting.php 顶部的支持驱动注释列表中新增了 mercure:
url,驱动会自动回退到 FrankenPHP 内置的 Mercure hub(即 mercure_publish() 函数)。该判断由 CreatesMercureDrivers::mercure() 完成:
url 应指向用于发布的管理 API,而 public_url 是浏览器实际连接的 URL。区分两者是为了支持这样的部署:发布走内部网络(如 Docker Compose),只把公开 URL 暴露给浏览器。
分离的发布与订阅令牌
Mercure 是一种以 JWT 进行访问控制的协议。CreatesMercureDrivers trait 会为发布(服务端 → hub)和订阅(浏览器 → hub)分别构建独立的令牌工厂:
secret、publish_secret、subscribe_secret可分别配置,缺省时回退到secretalgorithm、publish_algorithm、subscribe_algorithm同样可按方向单独设置(默认HS256)- HS256 要求密钥至少 32 字节,HS384 至少 48 字节,HS512 至少 64 字节;否则会抛出
InvalidArgumentException
*)持有 Grant::ACTION_PUBLISH 权限,并由 CachingTokenProvider 缓存,从而避免每次广播都重新生成 JWT。
通过单一 Cookie 完成频道授权
与 WebSocket 驱动最大的架构差异在于:订阅端的授权是通过一个 Cookie 完成的。MercureBroadcaster::auth() 接收一个 channel_names 数组(每次请求最多 100 项),对每个频道分别评估授权,然后统一签发一个覆盖所有频道的授权 Cookie:
denied: true,其余获授权的频道仍继续工作。这一点很重要,因为 Mercure 会在一条 EventSource 连接上多路复用许多主题,中途增加或移除频道时无需断开连接。
Presence 频道使用的 Grant 与普通 private/private-encrypted 频道不同,它针对的是 Mercure Subscription API 所使用的订阅 URL 模式。
端到端加密频道
以private-encrypted- 为前缀的频道被视为端到端加密(E2EE)——这意味着 Mercure hub 本身也看不到消息负载。这是现有 Pusher 与 Reverb 驱动都不具备的能力。
将 encryption_key 配置为 base64 编码的 32 字节密钥后,ChannelEncrypter 便会启用;在 broadcast() 发送之前,它会把事件名、载荷和 socket ID 一并封装为 JWE(JSON Web Encryption)。hub 中转的只是已加密的字节流。
auth() 响应返回的 jwk(JSON Web Key)字段进行解密——这是 Mercure 规范推荐的带外密钥交换方式,hub 始终无从得知密钥。
Presence 频道无法加密,因为成员列表本身就是通过 hub 的 Subscription API 流转的——这与 E2EE 在设计上无法共存。
Whisper(客户端之间的直接消息)
当client_events 启用(默认开启)时,每个受保护的频道都会分配一个专用的”whisper 主题”,订阅者可以向该主题发布消息:
主题命名
由于 Mercure hub 常常在多个应用之间共享,主题需要在topic_prefix 命名空间下进行隔离以避免冲突。默认值为:
.alt 是保留的、不可解析的 DNS 后缀(RFC 9476),因此绝不会与真实域名冲突。频道名会按照 RFC 3986 进行 URL 编码,并置于单个路径段中:
Cookie 域名与错误提示
由于 Mercure hub 常常运行在与应用不同的子域名下(例如mercure.example.com 对 app.example.com),当共享 Cookie 域名解析失败时,驱动会抛出信息明确的异常:
__Secure- 或 __Host- 前缀的 Cookie 名称,驱动还会在启动时校验 public_url 必须为 HTTPS:
在 Reverb、Pusher/Ably 与 Mercure 之间取舍
Mercure 适合那些不需要持久双向连接的场景——例如通知、进度反馈或以服务端向客户端单向推送为主的类聊天用例。如果你已经在使用 FrankenPHP,完全无需额外基础设施即可运行。
相关页面
- 广播基础
- Laravel Cloud(基于 FrankenPHP 的运行环境)