本文是基于laravel 5.4版本的廣播模塊代碼進(jìn)行分析書寫;
推薦:《laravel教程》
簡介
廣播是指發(fā)送方發(fā)送一條消息,訂閱頻道的各個(gè)接收方都能及時(shí)收到消息;比如 A同學(xué)寫了一篇文章,這時(shí)候 B同學(xué)在文章底下評論了,A同學(xué)在頁面上是不用刷新就能收到提示有文章被評論了,這個(gè)本質(zhì)上就是A同學(xué)收到了廣播消息,這個(gè)廣播消息是由B同學(xué)評論這個(gè)動作觸發(fā)了發(fā)送廣播消息;
在整個(gè)廣播行為中,有一個(gè)重要的概念叫頻道channel,頻道的類型有
●?公共頻道public
●?私有頻道private
●?存在頻道presence
移動端訂閱了公共頻道public,會直接提示成功;私有頻道private和存在頻道presence在進(jìn)行訂閱的過程中,會向服務(wù)器端發(fā)送權(quán)限驗(yàn)證,看是不是有權(quán)限可以訂閱該頻道;私有頻道private和存在頻道presence的區(qū)別在于,私有頻道private能夠接收其他成員發(fā)送的消息,而存在頻道presence除此之外,還能夠在用戶的加入與離開時(shí)接收信息;
廣播適合以下場景(此小部分摘自基于 Pusher 驅(qū)動的 laravel 事件廣播(上)):
●?通知(Notification) 或 信號(signal)
通知是最簡單的示例,也最經(jīng)常用到。信號也可看作是通知的一種展現(xiàn)形式,只不過信號沒有ui而已。
●?Activity Streams
Activity Streams(feeds)是社交網(wǎng)絡(luò)的核心。如微信朋友圈的點(diǎn)贊和評論,A可以實(shí)時(shí)看到B的點(diǎn)贊,B可以實(shí)時(shí)看到A的評論。
●?聊天
聊天信息的實(shí)時(shí)顯示
模塊組成
Demo
日志驅(qū)動
配置
.env文件修改或添加一行:BROADCAST_DRIVER=log;
廣播
直接調(diào)用
?$manager?=?app(IlluminateBroadcastingBroadcastManager::class); ?$driver?=?$manager->connection(); ?//?第一個(gè)參數(shù)是頻道名,第二個(gè)參數(shù)是事件名,第三個(gè)參數(shù)是廣播內(nèi)容 ?$driver->broadcast(['channel_1',?'channel_2'],?'login',?['message'?=>?'hello?world']);
因?yàn)槭侨罩掘?qū)動,所以廣播內(nèi)容會寫到框架配置的日志文件中,輸出消息如下所示
[2017-08-18?20:45:49]?local.INFO:?Broadcasting?[login]?on?channels?[channel_1,?channel_2]?with?payload: { ????"message":?"hello?world" }
監(jiān)聽事件廣播
這種調(diào)用方式,是當(dāng)實(shí)現(xiàn)ShouldBroadcast接口的事件被觸發(fā)時(shí),則會進(jìn)行廣播操作;(同時(shí),還有一個(gè)接口叫ShouldBroadcastNow,與ShouldBroadcast接口的不同在于,將實(shí)現(xiàn)ShouldBroadcastNow接口的事件放入隊(duì)列中時(shí),會被放入叫sync的隊(duì)列中)
舉個(gè)例子,
第一步,IlluminateAuthEventsLogin事件是用戶登錄成功后會觸發(fā)的事件,略作改動,讓其實(shí)現(xiàn)廣播功能;
class?Login?implements?ShouldBroadcast?{ ????...... ???? ????//?定義事件被觸發(fā)時(shí),廣播頻道;此處定義名為?first-channel?的私有頻道 ????public?function?broadcastOn()?{ ????????return?[ ????????????new?PrivateChannel('first-channel'), ????????]; ????} ???? ????//?自定義廣播名稱;如果方法未定義,默認(rèn)以類名為事件名,此處的默認(rèn)值是?IlluminateAuthEventsLogin ????public?function?broadcastAs()?{ ????????return?'login'; ????} }
第二步,注冊事件監(jiān)聽;在app/Providers/EventServiceProvider.php中修改:
protected?$listen?=?[ ???...... ???'IlluminateAuthEventsLogin'?=>?[ ????????'AppListenersUserLogin', ???], ];
文件app/Listeners/UserLogin.php粗糙地實(shí)現(xiàn)了一下:
class?UserLogin?{ ????public?function?__construct()?{} ???? ????public?function?handle(Login?$event){ ????????Log::info('Do?UserLogin?Listener:?I?was?Login'); ????} }
第三步,觸發(fā)事件,發(fā)送廣播;有好幾種觸發(fā)廣播方式:
1.直接事件觸發(fā)
event(new?IlluminateAuthEventsLogin($user,?true));
2.幫助函數(shù)broadcast,間接觸發(fā)事件
broadcast(new?IlluminateAuthEventsLogin($user,?true));
3.廣播管理類,間接觸發(fā)事件,直接廣播
$manager?=?app(IlluminateBroadcastingBroadcastManager::class); $manager->event(new?IlluminateAuthEventsLogin($user,?true));
4.廣播管理類,間接觸發(fā)事件,放入隊(duì)列
$manager?=?app(IlluminateBroadcastingBroadcastManager::class); $manager->queue(new?IlluminateAuthEventsLogin($user,?true));
Pusher驅(qū)動
Pusher是一個(gè)第三方服務(wù),服務(wù)器發(fā)送廣播時(shí),會向Pusher發(fā)送請求,再通過Pusher與瀏覽器或移動端保持的長連接進(jìn)行數(shù)據(jù)交互;
配置
通過Pusher官網(wǎng)注冊用戶信息,獲取屬于自已的一套密鑰信息,修改.env的配置文件;
BROADCAST_DRIVER=pusher PUSHER_APP_ID=xxxxxxxxxxxxxxxxxxxxxx PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxxxx PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxx
準(zhǔn)備工作
事件監(jiān)聽
后臺的事件監(jiān)聽還是采用”日志驅(qū)動”部分的登錄例子;
前端
前端頁面引入以下代碼:
<script></script><script> // 打開 Pusher 的調(diào)試日志 Pusher.logToConsole = true; // 定義 Pusher 變量 var pusher = new Pusher('PUSHER_APP_KEY的值', { cluster: 'ap1', encrypted: true }); // 定義頻道,綁定事件 var channel = pusher.subscribe('private-first-channel'); channel.bind('login', function(data) { alert(data); }); </script>
如果訂閱的是公共頻道,則不會向服務(wù)器端請求權(quán)限檢查;如果是私有頻道(頻道名是以private-開頭)或存在頻道(頻道名是以presence-開頭),則會發(fā)出權(quán)限檢查請求;對應(yīng)的后端需要定義私有頻道和存在頻道的權(quán)限;
頻道權(quán)限定義
頻道的權(quán)限定義是在routes/channels.php里;此處筆者為first-channel頻道定義權(quán)限回調(diào)函數(shù):
Broadcast::channel('first-channel',?function?($user)?{ ????return?(int)?$user->id?===?1; });
有讀者會疑問,前端頁面訂閱的頻道不是private-first-channel嗎?怎么后端只定義first-channel頻道的權(quán)限呢?那是因?yàn)椋蠖硕x的頻道假設(shè)是A,那么在Pusher及瀏覽器端或移動端傳遞的私有頻道名為private-A,存在頻道則會是presence-A;
廣播
直接廣播
$manager?=?app(IlluminateBroadcastingBroadcastManager::class); $driver?=?$manager->connection(); //?socket?參數(shù)是廣播私有頻道時(shí)排除的?socket,?每個(gè)瀏覽器端或者移動端在建立?websocket?時(shí)都會被分配一個(gè)?socket_id $driver->broadcast(['private-first-channel'],?'login',?['user'?=>?['name'?=>?'hello'],?'socket'?=>?'5395.4377611']);
間接廣播
參考“日志驅(qū)動”提及的間接廣播方式;
如果要發(fā)送排我廣播(也就是除了當(dāng)前請求的這個(gè)客戶端不收到廣播消息),則需要以下條件:
1.事件使用IlluminateBroadcastingInteractsWithSockets trait;
2.前端發(fā)送過來的請求頭部要攜帶X-Socket-ID信息;
3.事件觸發(fā)執(zhí)行broadcast(new IlluminateAuthEventsLogin($user, true))->toOthers();
redis驅(qū)動
配置
.env文件修改或添加一行:BROADCAST_DRIVER=redis;
廣播
原理是同樣在后端部署一個(gè)Socket.IO服務(wù)器,Laravel框架會發(fā)布消息到Socket.IO服務(wù)器上,由Socket.IO服務(wù)器同瀏覽器端或者移動端保持長連接;
這部分筆者尚未demo,網(wǎng)上入門資料還是挺多的,知道原理,這部分動作上手就容易多了;