Skip to main content
本页介绍的 MercureBroadcasterlaravel/framework PR #61474(2026 年 9 月 10 日合并)引入。截至撰写时,该功能尚未包含在任何标签发布中,官方文档也未收录。本文是基于源码和 docblock 的前瞻性解读,实际使用前请查阅 laravel/framework 的变更记录。

概述

长期以来,Laravel 的广播层提供了 Reverb、Pusher、Ably 这类”WebSocket 原生”驱动,而现在 config/broadcasting.phpdriver 中新增了 mercure Mercure 是一种基于 Server-Sent Events (SSE) 的实时通信协议。它并非使用 WebSocket 那样的双向连接,而是运行在类似 HTTP/1.1 或 HTTP/2 长轮询之上,因此具有以下特点:
  • 可以透明地穿越常见的 HTTP 基础设施(反向代理、CDN、负载均衡器)
  • 客户端只需浏览器内置的 EventSource API 即可实现,无需专门的客户端库
  • FrankenPHP 内置了 Mercure hub,无需额外基础设施即可运行

新增的 driver

config/broadcasting.php 顶部的支持驱动注释列表中新增了 mercure
同时还提供了连接配置示例:
若省略 url,驱动会自动回退到 FrankenPHP 内置的 Mercure hub(即 mercure_publish() 函数)。该判断由 CreatesMercureDrivers::mercure() 完成:
当你运行外部 hub 时,url 应指向用于发布的管理 API,而 public_url 是浏览器实际连接的 URL。区分两者是为了支持这样的部署:发布走内部网络(如 Docker Compose),只把公开 URL 暴露给浏览器。

分离的发布与订阅令牌

Mercure 是一种以 JWT 进行访问控制的协议。CreatesMercureDrivers trait 会为发布(服务端 → hub)和订阅(浏览器 → hub)分别构建独立的令牌工厂
  • secretpublish_secretsubscribe_secret 可分别配置,缺省时回退到 secret
  • algorithmpublish_algorithmsubscribe_algorithm 同样可按方向单独设置(默认 HS256
  • HS256 要求密钥至少 32 字节,HS384 至少 48 字节,HS512 至少 64 字节;否则会抛出 InvalidArgumentException
发布令牌对所有主题(*)持有 Grant::ACTION_PUBLISH 权限,并由 CachingTokenProvider 缓存,从而避免每次广播都重新生成 JWT。 与 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 主题”,订阅者可以向该主题发布消息:
频道自身的主题保持仅服务端可写,因此客户端永远无法伪造来自服务端的事件。这一设计与 Pusher 的”client events”功能类似,但通过区分主题让权限划分更加清晰。

主题命名

由于 Mercure hub 常常在多个应用之间共享,主题需要在 topic_prefix 命名空间下进行隔离以避免冲突。默认值为:
.alt 是保留的、不可解析的 DNS 后缀(RFC 9476),因此绝不会与真实域名冲突。频道名会按照 RFC 3986 进行 URL 编码,并置于单个路径段中:
由于 Mercure hub 常常运行在与应用不同的子域名下(例如 mercure.example.comapp.example.com),当共享 Cookie 域名解析失败时,驱动会抛出信息明确的异常:
如果你使用了 __Secure-__Host- 前缀的 Cookie 名称,驱动还会在启动时校验 public_url 必须为 HTTPS:

在 Reverb、Pusher/Ably 与 Mercure 之间取舍

Mercure 适合那些不需要持久双向连接的场景——例如通知、进度反馈或以服务端向客户端单向推送为主的类聊天用例。如果你已经在使用 FrankenPHP,完全无需额外基础设施即可运行。

相关页面

最后修改于 2026年9月13日