摘要:今天,讓我們深入研究下的廣播系統。廣播系統的目的是用于實現當服務端完成某種特定功能后向客戶端推送消息的功能。這種使用場景可以完美詮釋廣播系統的工作原理。另外,本教程將使用廣播系統實現這樣一個即時通信應用。
這是一篇譯文,譯文首發于 Laravel 廣播系統工作原理,轉載請注明出處。
今天,讓我們深入研究下 Laravel 的廣播系統。廣播系統的目的是用于實現當服務端完成某種特定功能后向客戶端推送消息的功能。本文我們將學習如何使用第三方 Pusher 工具向客戶端推送消息的功能。
如果您遇到在 Laravel 中需要實現當服務器處理完成某項工作后向客戶端發送消息這類的功能,那么您需要使用到 Laravel 的廣播系統。
比如在一個支持用戶互相發送消息的即時通信應用,當用戶 A 給用戶 B 發送一條消息時,系統需要實時的將消息推送給用戶 B,并且信息以彈出框或提示消息框形式展現給用戶 B。
這種使用場景可以完美詮釋 Laravel 廣播系統的工作原理。另外,本教程將使用 Laravel 廣播系統實現這樣一個即時通信應用。
或許您會對服務器是如何將消息及時的推送給客戶端的技術原理感興趣,這是因為在服務端實現這類功能時使用了套接字編程技術。在開始實現即時通信系統前,先讓我們了解下套接字編程的大致流程:
首先,服務器需要支持 WebSocket 協議,并且允許客戶端建立 WebSocket 連接;
您可以實現自己的 WebSocket 服務,或者使用第三方服務如 Pusher,后文會用到 Pusher 庫;
客戶端創建一個服務器的 Web Socket 連接,連接成功后客戶端會獲取唯一標識符;
一旦客戶端連接成功,表示該客戶端訂閱了指定頻道,將接收這個頻道的消息;
最后,客戶端還會注冊其所訂閱的頻道的監聽事件;
當服務端完成指定功能后,我們以指定頻道名稱和事件名稱的信息通知到 WebSocket 服務器;
最終,WebSocket 服務器將這個指定事件已廣播的形式推送到所有注冊這個頻道監聽的客戶端。
以上所涉及的內容看似很多,但通過本文學習您將掌握個中的訣竅。
接下來,讓我們打開 Laravel 默認廣播系統配置文件 config/broadcasting.php 看看里面的配置選項:
env("BROADCAST_DRIVER", "null"), /* |-------------------------------------------------------------------------- | Broadcast Connections |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used | to broadcast events to other systems or over websockets. Samples of | each available type of connection are provided inside this array. | */ "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"), "encrypted" => true, ], ], "redis" => [ "driver" => "redis", "connection" => "default", ], "log" => [ "driver" => "log", ], "null" => [ "driver" => "null", ], ], ];
默認情況下 Laravel 框架提供諸多開箱即用的廣播驅動器程序。
本文將使用 Pusher 作為廣播驅動器。但在調試階段,我們可以選擇使用 log 作為廣播驅動。同時如果選用 log 驅動,也就表示客戶端將不會接收任何消息,而只是將需要廣播的消息寫入到 laravel.log 日志文件內。
在下一節,我們將進一步講解如何實現一個即時通信應用。
前期準備Laravel 廣播系統支持 3 中不同頻道類型 - public(公共), private(私有) 和 presence(存在)。當系統需要向所用用戶推送信息時,可以使用 「public(公共)」 類型的頻道。相反,如果僅需要將消息推送給指定的頻道,則需要使用 「 private(私有)」 類型的頻道。
我們的示例項目將實現一個僅支持登錄用戶才能收到即時信息的消息系統,所以將使用 「 private(私有)」 類型的頻道。
開箱即用的認證服務首先對于新創建的 Laravel 項目,我們需要安裝 Laravel 提供的開箱即用的認證服務組件,默認認證服務功能包括:注冊、登錄等功能。如果您不知道如何使用默認認證服務,可以查看 Laravel 的用戶認證系統 文檔快速入門。
服務端 Pusher SDK 安裝配置這邊我們將使用 Pusher 這個第三方服務作為 WebSocket 服務器,所以還需要創建一個 帳號 并確保已獲取 API 證書。安裝配置遇到任何問題,請在評論區說明。
之后需要使用 Composer 包管理工具安裝 Pusher 的 PHP 版本 SDK,這樣才能在 Laravel 項目中使用 Pusher 發送廣播信息。
現在進入 Laravel 項目的根目錄,執行下面這條命令進行安裝:
composer require pusher/pusher-php-server "~3.0"
安裝完成后修改廣播配置文件,啟用 Pusher 驅動作為廣播系統的驅動器。
env("BROADCAST_DRIVER", "pusher"), /* |-------------------------------------------------------------------------- | Broadcast Connections |-------------------------------------------------------------------------- | | Here you may define all of the broadcast connections that will be used | to broadcast events to other systems or over websockets. Samples of | each available type of connection are provided inside this array. | */ "connections" => [ "pusher" => [ "driver" => "pusher", "key" => env("PUSHER_APP_KEY"), "secret" => env("PUSHER_APP_SECRET"), "app_id" => env("PUSHER_APP_ID"), "options" => [ "cluster" => "ap2", "encrypted" => true ], ], "redis" => [ "driver" => "redis", "connection" => "default", ], "log" => [ "driver" => "log", ], "null" => [ "driver" => "null", ], ], ];
如你所見,我們修改了默認驅動器。并且將 connections 節點的 Pusher 配置的 cluster 修改成 ap2。
同時還有需要從 .env 配置文件獲取的配置選項,所以我們需要更新 .env 文件,加入如下配置信息:
BROADCAST_DRIVER=pusher PUSHER_APP_ID={YOUR_APP_ID} PUSHER_APP_KEY={YOUR_APP_KEY} PUSHER_APP_SECRET={YOUR_APP_SECRET}
接下來,還需要對 Laravel 核心文件稍作修改才能使用最新的 Pusher SDK。不過,我并不提倡修改 Laravel 核心文件,這邊由于演示方便所以我修改了其中的代碼。
讓我們打開 vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php 文件。將 use Pusher; 替換為 use PusherPusher;。
之后打開 vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php 文件,在類似下面的代碼中做相同修改:
return new PusherBroadcaster( new PusherPusher($config["key"], $config["secret"], $config["app_id"], Arr::get($config, "options", [])) );
最后,在 config/app.php 配置中開啟廣播服務提供者配置:
AppProvidersBroadcastServiceProvider::class,
這樣 Pusher 庫的安裝工作就完成了。下一節,我們將講解客戶端類庫的安裝。
客戶端 Pusher 和 Laravel Echo 類庫的安裝配置在廣播系統中,客戶端接口負責連接 WebSocket 服務器、訂閱指定頻道和監聽事件等功能。
幸運的是 Laravel 已經給我們提供了一個叫 Laravel Echo 的插件,它實現一個復雜的 JavaScript 客戶端程,。并且這個插件內置支持 Pusher 的服務器連接。
可以通過 NPM 包管理器安裝 Laravel Echo 模塊。如果您還沒有安裝 Node.js 及 NPM 包管理程序,還是要先安裝 Node.js 才行。
這里我認為您已經安裝好了 Node.js,所以安裝 Laravel Echo 擴展的命令如下:
npm install laravel-echo
安裝完成后我們直接將 node_modules/laravel-echo/dist/echo.js 文件復制到 public/echo.js 就行了。
僅適用一個 echo.js 文件有點殺雞用了牛刀的感覺,所以您還可以到 Github 直接下載 echo.js 文件。
至此,我們就完成了客戶端組件的安裝。
服務端文件設置回想一下前文提到的內容:首先我們需要實現一個允許用戶互相發送消息的應用;另外,應用會通過廣播系統向已登錄系統并且有收到消息的用戶推送消息。
這一節我們將編寫服務端代碼實現廣播系統相關功能。
創建 message 遷移文件首先,我們需要創建一個 Message 模型用于存儲用戶發送的消息,執行如下命令創建一個遷移文件:
php make:model Message --migration
但在執行 migrate 命令前,我們需要在遷移文件中加入表字段 to、from 和 message。
increments("id"); $table->integer("from", false, true); $table->integer("to", false, true); $table->text("message"); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists("messages"); } }
然后運行 migrate 命令運行數據庫遷移文件:
php artisan migrate
當需要在 Laravel 執行事件時,我們首先需要做的是創建一個事件類,Laravel 將基于不同的事件類型執行不同的操作。
如果事件為一個普通事件,Laravel 會調用對應的監聽類。如果事件類型為廣播事件,Laravel 會使用 config/broadcasting.php 配置的驅動器將事件推送到 WebSocket 服務器。
本文使用的是 Pusher 服務,所以 Laravel 將事件推送到 Pusher 服務器。
先使用下面的 artisan 命令創建一個事件類:
php artisan make:event NewMessageNotification
這個命令會創建 app/Events/NewMessageNotification.php 文件,讓我們修改文件內的代碼:
message = $message; } /** * Get the channels the event should broadcast on. * * @return Channel|array */ public function broadcastOn() { return new PrivateChannel("user.".$this->message->to); } }
需要重點指出的是 NewMessageNotification 類實現了 ShouldBroadcastNow 接口,所以當我們觸發一個事件時,Laravel 就能夠立即知道有事件需要廣播給其他用戶了。
實際上,我們還可以去實現 ShouldBroadcast 接口,這個接口會將事件加入到消息隊列中。然后由隊列的 Worker 進程依據入隊順序依次執行。由于我們項目需要立即將消息推送給用戶,所以我們實現 ShouldBroadcastNow 接口更為合適。
還有就是我們需要顯示用戶接收的消息信息,所以我們將 Message 模型作為構造函數的參數,這樣消息信息就會同事件一起傳入到指定頻道。
接下來還在 NewMessageNotification 類中創建了一個 broadcastOn 方法,在該方法中定義了廣播事件的頻道名稱,因為只有登錄的用戶才能接收消息,所以這里創建了 PrivateChannel 實例作為一個私有頻道。
定義頻道名稱格式類似于 user.{USER_ID} ,其中包含了指向接收信息的用戶 ID,用戶ID 從 $this->message->to 中獲取。
對于客戶端程序需要先進行用戶身份校驗,然后才能驚醒連接 WebSocket 服務器處理;這樣才能保證私有頻道的消息僅會廣播給登錄用戶。同樣在客戶端也僅允許登錄用戶才能夠訂閱 user.{USER_ID} 私有頻道。
如果您在客戶端程序使用了 Laravel Echo 組件處理訂閱服務。那在客戶端代碼中僅需設置頻道路由即可,而無需關心用戶認證處理細節。
打開 routes/channels.php 文件,然后定義一個廣播路由:
id === (int) $id; }); Broadcast::channel("user.{toUserId}", function ($user, $toUserId) { return $user->id == $toUserId; });
以上,我們設置了名為 user.{toUserId} 路由,Broadcast::channel 方法的第二個參數接收一個閉包,Laravel 會將登錄用戶信息自動注入到閉包的第一個參數,第二個參數會從渠道中解析并獲取。
當客戶端嘗試訂閱 user.{USER_ID} 這個私有頻道時 Laravel Echo 組件會使用 XMLHttpRequest 以異步請求方式進行用戶身份校驗處理。
到這里即時通信所有編碼工作就完成了。
創建測試用例首先,創建一個控制器 app/Http/Controllers/MessageController.php:
middleware("auth"); } public function index() { $user_id = Auth::user()->id; $data = array("user_id" => $user_id); return view("broadcast", $data); } public function send() { // ... // 創建消息 $message = new Message; $message->setAttribute("from", 1); $message->setAttribute("to", 2); $message->setAttribute("message", "Demo message from user 1 to user 2"); $message->save(); // 將 NewMessageNotification 加入到事件 event(new NewMessageNotification($message)); // ... } }
接下來創建 index 路由所需的 broadcast 視圖文件 resources/views/broadcast.blade.php:
Test New notification will be alerted realtime!
之后,打開 routes/web.php 路由配置文件定義 HTTP 路由:
Route::get("message/index", "MessageController@index"); Route::get("message/send", "MessageController@send");
由于 MessageController 構造函數中使用了 auth 中間件,所以確保了僅有登錄用戶才能訪問以上路由。
接下來,讓我們分析下 broadcast 視圖文件的核心代碼:
視圖文件里首先,引入了 echo.js 和 pusher.min.js這兩個必要的模塊,這樣我們才能夠使用 Laravel Echo 去連接 Pusher 的服務器。
接著,創建 Laravel Echo 實例。
之后,通過 Echo 實例的 private 方法訂閱 user.{USER_ID} 這個私有頻道。之前我們說過只有登錄用戶才能訂閱私有頻道,所以 Echo 實例會使用 XHR 異步校驗用戶。然后,Laravel 會嘗試查找 user.{USER_ID} 路由,并匹配到已在 routes/channels.php 文件中定義的廣播路由。
一切順利的話,我們的項目此時即完成了 Pusher 服務器連接,之后就會監聽 user.{USER_ID} 頻道。這樣客戶端才可以正常接收指定頻道的所有消息。
完成客戶端接收 WebSocket 服務器消息接收編碼工作后,在服務端需要通過 Message::send 方法發送一個廣播消息。
發送的代碼如下:
public function send() { // ... // 創建消息 $message = new Message; $message->setAttribute("from", 1); $message->setAttribute("to", 2); $message->setAttribute("message", "Demo message from user 1 to user 2"); $message->save(); // 將 NewMessageNotification 加入到事件 event(new NewMessageNotification($message)); // ... }
這段代碼先是模擬了登錄用戶發送消息的操作。
然后通過 event 輔助函數將 NewMessageNotification 事件類實例加入廣播頻道。由于 NewMessageNotification 是 ShouldBroadcastNow 類的實例,Laravel 會從 config/broadcasting.php 配置文件中讀取廣播配置數據,然后將 NewMessageNotification 事件分發到配置文件所配置的 WebSocket 服務器的 user.{USER_ID} 頻道。
對于本文示例會將消息廣播到 Pusher 服務器的 user.{USER_ID} 頻道里。如果訂閱者的 ID 是 1,事件所處的廣播頻道則為 user.1。
之前我們已經在前端代碼中完成頻道的訂閱和監聽處理,這里當用戶收到消息時會在頁面彈出一個消息框提示給用戶。
現在如何對以上功能進行測試呢?
在瀏覽器訪問地址 http://your-laravel-site-doma... 。如果您未登錄系統,請先進行登錄處理,登錄后就可以看到廣播頁面信息了。
雖然現在的 Web 頁面看起來什么也沒有做,但是 Laravel 已經在后臺進行了一系列處理。通過 Pusher 組件的 Pusher.logToConsole 我們可以開啟 Pusher 的調試功能。下面是登錄后的調試信息內容:
Pusher : State changed : initialized -> connecting Pusher : Connecting : {"transport":"ws","url":"wss://ws-ap2.pusher.com:443/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0&flash=false"} Pusher : Connecting : {"transport":"xhr_streaming","url":"https://sockjs-ap2.pusher.com:443/pusher/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0"} Pusher : State changed : connecting -> connected with new socket ID 1386.68660 Pusher : Event sent : {"event":"pusher:subscribe","data":{"auth":"c91c1b7e8c6ece46053b:cd8b924580e2cbbd2977fd4ef0d41f1846eb358e9b7c327d89ff6bdc2de9082d","channel":"private-user.2"}} Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"private-user.2"} Pusher : No callbacks on private-user.2 for pusher:subscription_succeeded
可以看到我們完成了 WebSocket 服務器連接和私有頻道監聽。當然您看到的頻道名稱獲取和我的不一樣,但內容大致相同。接下來不要關閉這個 Web 頁面,然后去訪問 send 方法發送消息。
新開一個頁面窗口在瀏覽器訪問 http://your-laravel-site-doma... 頁面,順利的話會在 http://your-laravel-site-doma... 頁面收到一個提示消息。
同時在 index 的控制臺您還將看到到如下調試信息:
Pusher : Event recd : {"event":"AppEventsNewMessageNotification","data":{"message":{"id":57,"from":1,"to":2,"message":"Demo message from user 1 to user 2","created_at":"2018-01-13 07:10:10","updated_at":"2018-01-13 07:10:10"}},"channel":"private-user.2"}
如你所見,調試信息告訴我們我們接收來自 Pusher 服務器的 private-user.2 頻道的 AppEventsNewMessageNotification 消息。
當然,我們還可以通過 Pusher 管理后臺的儀表盤看到這個消息內容,它在 Debug Console 標簽頁,我們可以看到如下日志信息。
這就是今天的全部內容,希望能給大家帶來幫助。
結論今天,我們研究了 Laravel 的 廣播 這個較少使用的特性。廣播可以讓我們使用 Web Sockets 發送實時消息。此外我們還使用廣播功能實現了一個簡單的實時消息推送項目。本文內容較多,需要一些時間消化,有任何問題可以隨時聯系我。
原文:How Laravel Broadcasting Works
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/30814.html
摘要:外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。將使用者與子系統從直接耦合,轉變成由外觀類提供統一的接口給使用者使用,以降低客戶端與子系統之間的耦合度。接下來將深入分析外觀服務的加載過程。引導程序將在處理請求是完成引導啟動。 本文首發于 深入淺出 Laravel 的 Facade 外觀系統,轉載請注明出處。 今天我們將學習 Laravel 核心架構中的另一個主題「Fac...
摘要:劃下重點,服務容器是用于管理類的依賴和執行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發于 深入剖析 Laravel 服務容器,轉載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現給用戶的工作原理。 本章將帶領大...
摘要:年,由北京的團隊開發了涉足社交開源行業。終于,在年下半年,我們決定重寫這個程序,拋棄之前的每一行代碼。起初,我們選擇在中做生成函數,配合第三方包實現,功能實現了。的拓展不能直接以這種方式使用喲,因為我們做這個的想法是把配置移交到后臺配置。 什么是 ThinkSNS+ 好吧,這不是廣告。。。在 09 年,由北京的團隊開發了 ThinkSNS 涉足社交開源行業。09 年,由北京的團隊開發了...
摘要:如何實現持久化持久化,將在內存中的的狀態保存到硬盤中,相當于備份數據庫狀態。相當于備份數據庫接收到的命令,所有被寫入的命令都是以的協議格式來保存的。 最近社區里面有一篇文章引起了最多程序猿的關注,Laravel、PHPer 面試可能會遇到的問題,看評論區不少小伙伴們被難倒,對于一些問題同樣難倒了我(其實有很多啦),趁著周末有空,又總結梳理了一遍,順便來答一波題。由于個人技術水平有限,答...
閱讀 3290·2021-09-09 11:39
閱讀 1237·2021-09-09 09:33
閱讀 1139·2019-08-30 15:43
閱讀 555·2019-08-29 14:08
閱讀 1741·2019-08-26 13:49
閱讀 2386·2019-08-26 10:09
閱讀 1553·2019-08-23 17:13
閱讀 2291·2019-08-23 12:57