前言 在現代 Web 應用中,即時互動功能已成為提升使用者體驗的關鍵。從即時通知、聊天室到協作編輯,WebSocket 技術為伺服器與客戶端之間的雙向通訊提供了高效的解決方案。本文將深度解析如何在 Laravel 框架中,利用 laravel-websockets
套件實現強大的即時功能,並提供常見使用場景的詳細實作指南。
WebSocket 基礎與 laravel-websockets
設定 WebSocket vs. HTTP Polling 在 WebSocket 出現之前,實現即時通訊通常依賴 HTTP 輪詢(Polling),但這種方式效率低下且浪費資源。WebSocket 提供了一個持久性的單一 TCP 連線,允許伺服器和客戶端隨時進行雙向數據傳輸。
sequenceDiagram
participant Client
participant Server
Note over Client, Server: HTTP 長輪詢 (Long Polling)
Client->>Server: Request
Server-->>Client: No updates, wait...
Server->>Client: Data available, Response
Client->>Server: New Request (cycle repeats)
Note over Client, Server: WebSocket
Client->>Server: Upgrade Request (Handshake)
Server-->>Client: Upgrade Response (Connection established)
loop Bi-directional Communication
Server->>Client: Push Data
Client->>Server: Send Data
end
laravel-websockets
套件安裝與設定laravel-websockets
是一個基於 Ratchet 的純 PHP WebSocket 伺服器,它與 Laravel Broadcasting 系統無縫整合,並提供了 Pusher API 的完整替代方案。
步驟一:安裝套件
1 composer require beyondcode/laravel-websockets
步驟二:發布設定檔與遷移檔案
1 2 3 php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations" php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config" php artisan migrate
步驟三:設定 .env
與 config/broadcasting.php
首先,在 config/broadcasting.php
中設定 Pusher 連線:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 'connections' => [ 'pusher' => [ 'driver' => 'pusher' , 'key' => env ('PUSHER_APP_KEY' ), 'secret' => env ('PUSHER_APP_SECRET' ), 'app_id' => env ('PUSHER_APP_ID' ), 'options' => [ 'cluster' => env ('PUSHER_APP_CLUSTER' ), 'host' => env ('PUSHER_HOST' , '127.0.0.1' ), 'port' => env ('PUSHER_PORT' , 6001 ), 'scheme' => env ('PUSHER_SCHEME' , 'http' ), 'encrypted' => false , 'useTLS' => env ('PUSHER_SCHEME' ) === 'https' , ], ], ],
接著,更新 .env
檔案:
1 2 3 4 5 6 7 BROADCAST_DRIVER=pusher PUSHER_APP_ID=my-app-id PUSHER_APP_KEY=my-app-key PUSHER_APP_SECRET=my-app-secret PUSHER_HOST=127.0.0.1 PUSHER_PORT=6001 PUSHER_SCHEME=http
步驟四:設定前端 Laravel Echo
安裝 Echo 和 Pusher 客戶端:
1 npm install --save-dev laravel-echo pusher-js
在 resources/js/bootstrap.js
中設定 Echo:
1 2 3 4 5 6 7 8 9 10 11 12 13 import Echo from 'laravel-echo' ;window .Pusher = require ('pusher-js' );window .Echo = new Echo ({ broadcaster : 'pusher' , key : process.env .MIX_PUSHER_APP_KEY , cluster : process.env .MIX_PUSHER_APP_CLUSTER , wsHost : window .location .hostname , wsPort : 6001 , forceTLS : false , disableStats : true , });
核心運作機制:事件與頻道 Laravel 的即時通訊基於事件廣播 和頻道 訂閱機制。
graph TD
A[後端觸發事件] --> B[Laravel Event System]
B --> C{ShouldBroadcast}
C -->|Yes| D[Dispatch to Queue]
D --> E[Broadcast Driver]
E --> F[WebSocket Server]
F --> G[推送至指定頻道]
subgraph "前端"
H[Laravel Echo] --> I[訂閱頻道]
I --> J[監聽事件]
J --> K[更新 UI]
end
G --> I
頻道類型
公開頻道 (Public Channels) :無需授權,任何人都可以訂閱。適合廣播公開資訊。
私有頻道 (Private Channels) :需要授權才能訂閱。適合一對一的通知或聊天。
存在頻道 (Presence Channels) :基於私有頻道,但額外提供訂閱者列表,可用於顯示「誰在線上」。
場景一:即時通知系統 這是最常見的 WebSocket 應用,例如當有新訂單或新訊息時,即時通知使用者。
步驟一:建立事件 1 php artisan make:event NewNotificationEvent
步驟二:實作事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 namespace App \Events ;use Illuminate \Broadcasting \Channel ;use Illuminate \Broadcasting \InteractsWithSockets ;use Illuminate \Broadcasting \PrivateChannel ;use Illuminate \Contracts \Broadcasting \ShouldBroadcast ;use Illuminate \Foundation \Events \Dispatchable ;use Illuminate \Queue \SerializesModels ;class NewNotificationEvent implements ShouldBroadcast { use Dispatchable , InteractsWithSockets , SerializesModels ; public $message ; private $userId ; public function __construct ($userId , $message ) { $this ->userId = $userId ; $this ->message = $message ; } public function broadcastOn ( ) { return new PrivateChannel ('notifications.' . $this ->userId); } public function broadcastAs ( ) { return 'new.notification' ; } }
步驟三:設定頻道授權 在 routes/channels.php
中定義授權邏輯:
1 2 3 4 5 6 7 use Illuminate \Support \Facades \Broadcast ;Broadcast ::channel ('notifications.{userId}' , function ($user , $userId ) { return (int ) $user ->id === (int ) $userId ; });
步驟四:觸發事件 在控制器或服務中觸發事件:
1 2 3 4 5 6 use App \Events \NewNotificationEvent ;$userId = 1 ;$message = '您有一筆新訂單!' ;event (new NewNotificationEvent ($userId , $message ));
步驟五:前端監聽事件 1 2 3 4 5 6 7 8 9 const userId = 1 ; Echo .private (`notifications.${userId} ` ) .listen ('.new.notification' , (e ) => { console .log (e.message ); alert ('新通知:' + e.message ); });
場景二:即時聊天室 聊天室是 WebSocket 的典型應用,我們將使用存在頻道來實現。
步驟一:建立聊天事件 1 php artisan make:event NewChatMessage
步驟二:實作事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 namespace App \Events ;use App \Models \User ;use Illuminate \Broadcasting \InteractsWithSockets ;use Illuminate \Broadcasting \PresenceChannel ;use Illuminate \Contracts \Broadcasting \ShouldBroadcast ;use Illuminate \Foundation \Events \Dispatchable ;use Illuminate \Queue \SerializesModels ;class NewChatMessage implements ShouldBroadcast { use Dispatchable , InteractsWithSockets , SerializesModels ; public $message ; public $user ; private $chatRoomId ; public function __construct ($chatRoomId , User $user , $message ) { $this ->chatRoomId = $chatRoomId ; $this ->user = $user ; $this ->message = $message ; } public function broadcastOn ( ) { return new PresenceChannel ('chat.' . $this ->chatRoomId); } }
步驟三:設定頻道授權 1 2 3 4 5 6 7 Broadcast ::channel ('chat.{chatRoomId}' , function ($user , $chatRoomId ) { if ($user ->canJoinChatRoom ($chatRoomId )) { return ['id' => $user ->id, 'name' => $user ->name]; } });
步驟四:觸發事件 1 2 3 4 5 6 7 8 9 10 11 12 13 public function sendMessage (Request $request , $chatRoomId ) { $user = $request ->user (); $message = $request ->input ('message' ); broadcast (new NewChatMessage ($chatRoomId , $user , $message ))->toOthers (); return response ()->json (['status' => 'Message sent!' ]); }
注意 :->toOthers()
方法可以避免訊息發送者自己收到廣播。
步驟五:前端監聽與互動 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const chatRoomId = 123 ;Echo .join (`chat.${chatRoomId} ` ) .here ((users ) => { console .log ('目前在線:' , users); }) .joining ((user ) => { console .log (user.name , '加入了聊天室' ); }) .leaving ((user ) => { console .log (user.name , '離開了聊天室' ); }) .listen ('NewChatMessage' , (e ) => { console .log (e.user .name + ': ' + e.message ); }) .error ((error ) => { console .error ('頻道授權失敗:' , error); });
場景三:即時儀表板更新 對於需要即時監控數據的儀表板(如銷售額、網站流量),WebSocket 是絕佳選擇。
步驟一:建立事件 1 php artisan make:event DashboardUpdate
步驟二:實作事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 namespace App \Events ;use Illuminate \Broadcasting \Channel ;use Illuminate \Contracts \Broadcasting \ShouldBroadcast ;class DashboardUpdate implements ShouldBroadcast { use Dispatchable , InteractsWithSockets , SerializesModels ; public $data ; public function __construct ($data ) { $this ->data = $data ; } public function broadcastOn ( ) { return new Channel ('dashboard-updates' ); } }
步驟三:觸發事件 當有新數據產生時(例如,一筆新訂單完成),觸發事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class OrderService { public function completeOrder (Order $order ) { $dashboardData = [ 'total_sales' => Sales ::sum ('amount' ), 'new_orders_count' => Order ::whereDate ('created_at' , today ())->count (), ]; broadcast (new DashboardUpdate ($dashboardData )); } }
步驟四:前端監聽 1 2 3 4 5 6 7 8 9 10 11 Echo .channel ('dashboard-updates' ) .listen ('DashboardUpdate' , (e ) => { console .log ('儀表板數據更新:' , e.data ); updateDashboard (e.data ); }); function updateDashboard (data ) { document .getElementById ('total-sales' ).innerText = data.total_sales ; document .getElementById ('new-orders-count' ).innerText = data.new_orders_count ; }
部署與維護 啟動 WebSocket 伺服器 在開發環境中,可以直接使用 Artisan 命令啟動:
1 php artisan websockets:serve
使用 Supervisor 維持伺服器運行 在生產環境中,需要一個進程管理器來確保 WebSocket 伺服器持續運行。
1 2 3 4 5 6 7 8 9 10 11 [program:laravel-websockets] process_name =%(program_name)s_%(process_num)02 dcommand =php /path/to/your/project/artisan websockets:serveautostart =true autorestart =true user =your-usernumprocs =1 redirect_stderr =true stdout_logfile =/path/to/your/project/storage/logs/websockets.log
Nginx 反向代理設定 為了使用標準的 80 和 443 埠,並支援 SSL,需要設定反向代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 server { location /app { proxy_pass http://127.0.0.1:6001/app; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection "Upgrade" ; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; } }
總結 Laravel Websockets 為開發即時應用提供了強大而優雅的解決方案。通過掌握事件廣播、頻道授權和前端 Echo 監聽的核心概念,您可以輕鬆實現從簡單的即時通知到複雜的互動式聊天室等多種功能。在實作時,務必根據場景選擇合適的頻道類型,並在生產環境中做好部署和維護,以確保系統的穩定性和安全性。
學習建議
從私有頻道開始 :私有頻道是學習頻道授權的最佳起點。
善用儀表板 :laravel-websockets
套件自帶一個儀表板(預設路徑 /laravel-websockets
),可以用於調試和監控。
注意非同步 :廣播事件最好在佇列中執行,以避免阻塞使用者請求。在事件類別中實現 ShouldQueue
介面即可。