mybid
即時競標 · 10 分鐘閱讀 · 4 次閱讀

Laravel Reverb 做即時競標:從連線、頻道到出價廣播的完整拆解

Reverb 讓 Laravel 專案不必再依賴外部推播服務。這篇拆解頻道設計、廣播時機、Livewire 接收方式與實際能撐多少連線。

Laravel Reverb 做即時競標:從連線、頻道到出價廣播的完整拆解

一句話總結:Laravel Reverb 是官方推出的 WebSocket 伺服器,讓拍賣系統不必付費給外部推播服務就能做到即時出價更新,但要用得穩,關鍵在頻道授權設計、廣播事件不進佇列、以及部署時把它當成獨立服務對待。

第一次把出價廣播接起來的那個下午我還記得。開兩個瀏覽器視窗,左邊出價,右邊的數字瞬間跳動——那種「它真的活了」的感覺很難形容。

然後我把測試機的人數拉到五十個同時連線,發現有些視窗的價格卡在三十秒前。查了半天,問題不在 Reverb,在我把廣播事件丟進了佇列,而佇列裡塞滿了得標通知信。

這種坑不踩過不會知道。這篇把用 Reverb 做即時競標會遇到的關鍵決策整理起來——至於整套系統的其他部分怎麼搭,用 Laravel 打造拍賣系統那篇談得比較全面,這裡只專注在即時推播這一層。

Laravel Reverb 是什麼?跟 Pusher、Socket.io 差在哪

Laravel Reverb 是 Laravel 官方的第一方 WebSocket 伺服器,用 PHP 寫成,跑在獨立的程序上,專門負責維持瀏覽器的長連線並把事件推送出去。它的協定跟 Pusher 相容,所以前端那套 Echo 的用法幾乎不用改。

三者的差別主要在營運模式:

方案 型態 成本結構 連線數上限 維運負擔
Reverb 自架,官方維護 只有主機成本 取決於機器規格 要自己顧程序存活
Pusher 雲端服務 依連線數與訊息量計費 依方案 幾乎沒有
Socket.io 自架,需 Node 環境 主機成本 多一套技術棧要維護

對拍賣平台來說,成本結構的差異會隨規模放大得很明顯。推播服務通常按「訊息數 × 連線數」計費,而拍賣的特性是結標前那幾分鐘訊息量會暴衝——一件熱門商品在最後三分鐘有五十個人盯著、出價二十次,就是一千則訊息,而你可能同時有幾百件商品在結標。

自架的代價是你得自己確保那個程序活著。這件事不難,但要記得做:程序管理工具、健康檢查、掛掉自動重啟。我看過的 Reverb 事故,九成都是「程序死了沒人發現」,不是 Reverb 本身有問題。

Reverb、Pusher 與 Socket.io 三種即時推播方案在型態、成本結構與維運負擔上的差異對比

出價廣播為什麼不能進佇列?

因為佇列的延遲不可控,而出價更新晚一秒就等於系統壞了。

Laravel 的事件廣播預設會進佇列非同步處理,這個預設值對絕大多數場景都對——寄信、寫日誌、更新統計,晚幾秒沒人在意。但出價廣播是例外。

問題出在佇列是共用的。你的出價事件排在佇列裡,前面可能有兩百封待寄的得標通知信。worker 一封一封處理,輪到出價廣播時已經過了三十秒。這時候使用者看到的價格是舊的,他照著出價,結果被拒絕——他會認為平台有問題,而且他沒有錯。

解法是讓出價事件跳過佇列直接廣播。Laravel 有專門的介面做這件事,實作上就是換一個介面的差別,但這個決定的影響大到不成比例

那哪些該進佇列?判斷準則很簡單:

  • 使用者正盯著螢幕等這件事發生嗎? → 是,同步廣播
  • 這件事晚三十秒有人會發現嗎? → 不會,進佇列

照這個準則分:出價廣播、倒數延長通知、拍賣結束事件要同步;得標通知信、賣家結算、排行榜更新、數據統計進佇列。

出價廣播的資料流:從使用者出價、鎖檢查、寫入到 Reverb 推播至所有瀏覽器,並區分同步與佇列處理的事件

還有一個相關的坑:廣播的內容要夠完整,讓前端不用再回頭查一次資料庫。如果廣播只送「有新出價」而不帶金額,每個瀏覽器收到後都會發一個請求回來問價格——五十個人在線,就是瞬間五十個請求。訊息裡直接帶上新價格、出價者暱稱、剩餘時間,這個問題就消失了。

頻道怎麼設計才不會出事?

每件拍賣品一個私有頻道,授權邏輯放在頻道定義裡,不要用公開頻道圖方便。

頻道粒度的選擇有兩種常見錯誤:

錯誤一:全站一個頻道。 所有拍賣共用,前端自己過濾不相關的訊息。這樣寫最快,但代表每個使用者都會收到全站所有拍賣的出價事件。一百件商品同時進行,每個瀏覽器都在接收九十九件不關他事的訊息,頻寬和電池都在燒。

錯誤二:用公開頻道。 公開頻道不需要授權,任何人知道頻道名稱就能訂閱。拍賣的出價紀錄裡通常有使用者暱稱、出價金額、時間——這些資訊被爬走可以拿來分析競爭對手的出價習慣,甚至反推誰是活躍買家。

正確做法是每個拍賣品一個私有頻道,訂閱時要通過授權檢查。授權邏輯可以判斷:這個使用者有沒有登入、是不是被停權中、這件拍賣是否還在進行。

授權這一層還有個實務細節值得注意:授權請求是 HTTP 打回 Laravel 的,所以每個連線建立時都會有一次資料庫查詢。結標前湧入大量使用者時,這裡會變成瓶頸。把授權判斷需要的資料快取起來,能省下不少。

關於連線認證的更完整討論,即時競標的 WebSocket 架構那篇有更深入的說明。

Livewire 元件怎麼接收廣播?

Livewire 可以直接監聽 Echo 事件,收到之後重新渲染元件——不需要自己寫 WebSocket 的接收邏輯。

這是這套組合最舒服的地方。傳統做法是前端寫一段 JavaScript 接收訊息、解析、然後手動更新 DOM 上的價格、出價紀錄、按鈕狀態。用 Livewire 的話,元件宣告要監聽哪個頻道的哪個事件,收到就跑一個方法,方法裡重新載入資料,畫面自動更新。

實作上要注意三件事:

第一,重新渲染的範圍要小。 如果整個商品頁都是一個 Livewire 元件,每次有人出價就重繪整頁——包含那些沒變的商品照片和描述。應該把「出價區塊」獨立成自己的元件,只重繪那一塊。

第二,樂觀更新要小心。 使用者按下出價後,直覺會想立刻在畫面上顯示新價格,不等伺服器回應。但拍賣的出價可能會失敗(被別人搶先、金額不足、帳號受限),先顯示成功再改回去,體驗比等一下更差。這裡建議誠實一點:按下去顯示「處理中」,等結果回來再更新。

第三,斷線重連後要補資料。 WebSocket 斷線期間發生的出價,重連後不會補送。所以重連事件觸發時,元件要主動重新載入一次目前狀態,否則使用者會看到一個停在斷線那一刻的價格。這個情境在手機上特別常見——切到別的 App 再切回來,連線通常已經斷了。

出價區塊的介面設計本身也有不少學問,即時競標的介面設計有更完整的討論。

連線數會不會爆?部署要注意什麼

單台中等規格的機器撐幾千條連線是合理期待,真正的限制通常不是 Reverb 本身,而是系統的檔案描述符上限和記憶體。

部署時實際會碰到的幾件事:

  • 調高檔案描述符上限:每條 WebSocket 連線佔一個檔案描述符,系統預設值通常遠低於你需要的數字,這是第一個會撞到的牆
  • 反向代理要設定長連線:Nginx 預設的逾時會把閒置連線砍掉,WebSocket 需要特別設定升級標頭與逾時時間
  • 程序監控不可省:用程序管理工具顧著它,掛了自動重啟,並且要有告警
  • 橫向擴展需要共用狀態:跑多個 Reverb 實例時,事件要能跨實例傳遞,這需要額外設定

規劃容量時,一個實用的估法是:同時線上人數 ≈ 進行中的熱門拍賣數 × 每件平均關注人數。拍賣的流量分佈非常尖峰——平時可能只有幾十條連線,週日晚上多件商品同時結標時瞬間衝到幾千條。要按尖峰規劃,不是按平均。

如果團隊沒有維運 WebSocket 服務的經驗,這部分找有實務經驗的拍賣平台開發團隊協助評估架構,通常比上線後才發現撐不住划算。

Laravel Reverb 部署檢核清單:檔案描述符上限、反向代理長連線設定、程序監控與橫向擴展的共用狀態

上線前一定要測的四個情境

這四個情境涵蓋了絕大多數實際發生過的事故。

情境一:多人同時出價。 開十個以上的並行連線對同一件商品出價,確認最終只有一個最高價、所有人看到的數字一致、且沒有人的畫面停在錯誤狀態。

情境二:結標前的防狙擊延長。 在剩餘三十秒時出價觸發延長,確認所有連線中的使用者都收到新的結束時間,而不是只有出價者自己看到。這個情境最常出包,因為延長事件很容易被漏掉沒廣播。

情境三:斷線重連。 手動中斷連線三十秒,期間讓別人出價數次,然後恢復連線,確認畫面會補上正確的最新狀態。

情境四:Reverb 程序重啟。 直接把 Reverb 重啟,觀察前端是否自動重連、重連後資料是否正確。這模擬的是部署或當機的情況,使用者不該因此看到壞掉的頁面。

這四項建議做成自動化測試,而不是上線前手動點一次。 即時系統的問題特性是改 A 壞 B,沒有回歸測試很難維持穩定。

常見問題

Q:Reverb 可以跟現有的 Pusher 設定共存或切換嗎? 可以。Reverb 相容 Pusher 協定,切換主要是改設定檔和連線位址,應用層的廣播程式碼和前端 Echo 用法幾乎不用動。這也讓「先用 Pusher 上線、量大再換自架」成為可行的路線。

Q:一定要用 Livewire 嗎?用 Vue 或 React 可以嗎? 可以,Echo 是獨立的前端函式庫,任何框架都能接。選 Livewire 的理由是省掉維護一套獨立前端狀態的成本,如果團隊已經有前端框架的經驗和既有程式碼,沿用原本的更合理。

Q:出價訊息會不會遺失? WebSocket 本身不保證送達。實務上的做法是「廣播 + 定期校正」:靠廣播維持即時性,同時讓前端每隔一段較長的時間(例如三十秒)或在關鍵時刻(剩餘一分鐘時)主動同步一次真實狀態。這樣就算漏了一則訊息,也會很快被修正回來。

Q:本機開發時 Reverb 要一直開著嗎? 是的,它是獨立程序,跟 Laravel 的網頁伺服器分開跑。開發時忘記啟動的話,出價會成功但畫面不會即時更新——這個症狀很容易被誤判成程式有 bug,值得先確認程序有沒有在跑。

結語

Reverb 把即時推播從「要不要付錢給外部服務」變成「要不要多顧一個程序」,對拍賣這種訊息量會尖峰暴衝的應用來說,這個改變的經濟意義比技術意義還大。

但工具變簡單不代表問題消失。廣播該不該同步、頻道怎麼切、斷線怎麼補——這三件事跟你用哪套 WebSocket 方案無關,換成 Pusher 或 Socket.io 一樣要面對。

如果你正要開始接,我的建議是先把最單純的版本跑起來:一件商品、一個私有頻道、一個同步廣播事件,用兩個瀏覽器看到數字同步跳動。那個畫面出來之後,剩下的都是在解決規模和邊界情況的問題了。

相關文章

手機競標攻略:用手機參與拍賣的技巧與注意事項

從在外面用手機搶標的真實經驗出發,分析手機競標的優勢與限制、App 和行動網頁的差異、網路穩定性問題、手機出價的操作技巧,以及不可忽略的安全注意事項。

· 9 分鐘 · 133 次閱讀
閱讀 →

拍賣通知設定指南:不再錯過任何出價時機

從錯過心儀拍品的慘痛教訓出發,完整解析四大通知類型(出價/被超價/結標前/得標),教你在不同平台上設定最佳通知策略,同時避免通知疲勞,讓每一則提醒都發揮最大價值。

· 8 分鐘 · 145 次閱讀
閱讀 →

拍賣倒數計時攻略:最後幾分鐘的出價策略

從被狙擊的慘痛經驗出發,拆解拍賣最後 30 分鐘到倒數 10 秒的三階段出價策略,教你在倒數壓力下做出正確判斷、應對防狙擊延長,以及什麼時候該提早出手。

· 8 分鐘 · 872 次閱讀
閱讀 →