Skip to main content
本頁介紹的 MercureBroadcaster 是在 laravel/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()
若使用外部 Mercure hub,url 應指向用於發布的管理 API,而 public_url 則是瀏覽器所看到的 URL。之所以分開這兩者,是為了支援「透過內部網路(例如 Docker Compose)發布訊息,而只把公開 URL 暴露給瀏覽器」這類部署方式。

分離 publish 與 subscribe 兩種權杖

Mercure 是以 JWT (JSON Web Token) 進行存取控制的協定。CreatesMercureDrivers trait 會為 publish(伺服器 → hub)與 subscribe(瀏覽器 → hub)分別建立獨立的權杖工廠
  • secretpublish_secretsubscribe_secret 可以個別設定,未設定時會退回使用 secret
  • algorithmpublish_algorithmsubscribe_algorithm 同樣可個別設定(預設 HS256
  • HS256 要求 secret 至少 32 bytes、HS384 至少 48 bytes、HS512 至少 64 bytes,未達標會拋出 InvalidArgumentException
publish 權杖持有針對所有 topic(*)的 Grant::ACTION_PUBLISH 權限,並會由 CachingTokenProvider 記憶快取,避免每次廣播都要重新產生 JWT。 Mercure 與 WebSocket 驅動最大的架構差異,在於訂閱端的授權是透過單一 Cookie 完成的。MercureBroadcaster::auth() 會接收 channel_names 陣列(每個請求上限 100 個),逐一評估頻道授權,然後一次發出一個涵蓋所有頻道的授權 Cookie:
值得注意的重點是:單一頻道授權失敗,並不會讓整個回應失敗。被拒絕的頻道只會在回應中被標記為 denied: true,其他已授權的頻道仍可繼續訂閱。這樣的設計是因為 Mercure 在單一 EventSource 連線上多工承載多個 topic,所以要能讓頻道在連線中途新增或移除,同時不中斷既有的連線。 Presence 頻道會發出與一般 private / private-encrypted 頻道不同形狀的 Grant,其 subscribe 權限對應到 Mercure Subscription API 所使用的訂閱 URL 樣式。

端對端加密頻道

private-encrypted- 為前綴的頻道會被視為端對端加密(E2EE)頻道 —— 也就是說,連 Mercure hub 本身都看不到 payload。這是既有 Pusher 與 Reverb 驅動所沒有的能力。 只要將 encryption_key 設定為 base64 編碼的 32 bytes 金鑰,ChannelEncrypter 就會啟用,並在 broadcast() 送出訊息時,將事件名稱、payload 與 socket ID 一併封裝成 JWE(JSON Web Encryption)。hub 中繼的只會是加密後的位元組資料。
訂閱端則透過 auth() 回應中攜帶的 jwk(JSON Web Key)欄位,在瀏覽器內完成解密。這是 Mercure 規範建議的「將金鑰放在授權回應中以帶外方式傳遞」機制,hub 完全不會知道金鑰內容。
Presence 頻道無法加密,因為成員列表本來就需要透過 hub 的 Subscription API 流動,這在設計上就與 E2EE 不相容。

Whisper(客戶端之間的直接訊息)

client_events 啟用時(預設為 true),每個受保護的頻道都會被指派一個專屬的「whisper topic」,訂閱者僅能對該 topic 擁有 publish 權限:
頻道本身的 topic 仍為伺服器專用,因此客戶端不可能偽造成伺服器發出的事件。這樣的設計類似 Pusher 的「client events」功能,但透過分離 topic 讓權限劃分更為清楚。

Topic 命名規則

由於 Mercure hub 常常被多個應用共用,因此 topic 會以 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日