电竞比分网-中国电竞赛事及体育赛事平台

分享

【Laravel系列7.8】廣播系統(tǒng)

 硬核項(xiàng)目經(jīng)理 2022-06-20 發(fā)布于湖南

廣播系統(tǒng)

廣播系統(tǒng)指的是什么呢?在這里我們說(shuō)的廣播系統(tǒng)其實(shí)就是配合 WebSocket 實(shí)現(xiàn)的即時(shí)更新接口。什么意思呢?比如說(shuō)在你的購(gòu)物 App 上,如果訂單狀態(tài)發(fā)生了變化,比如賣(mài)家發(fā)貨了,那么馬上就會(huì)收到一條通知信息。當(dāng)然,App 上使用的不是 WebSocket ,而是不同平臺(tái)的推送機(jī)制,但它也是一種廣播通知機(jī)制。如果你對(duì) Redis 比較了解的話,也可以這么理解:它和 Redis 中的 Pub/Sub 也非常像,前端 SUBSCRIBE 監(jiān)聽(tīng)頻道,后端向頻道里 PUBLISH 數(shù)據(jù),就是這么個(gè)過(guò)程。

在 Web 頁(yè)面開(kāi)發(fā)的領(lǐng)域,現(xiàn)在 WebSocket 可以說(shuō)已經(jīng)相當(dāng)于是事實(shí)標(biāo)準(zhǔn)了。之前我們?nèi)绻诤笈_(tái)做上一個(gè)廣播通知功能的話,都是使用 Ajax 去輪詢請(qǐng)求,但現(xiàn)在這么做的人已經(jīng)不多了,畢竟 WebSocket 是更加可靠和高效的選擇。至于說(shuō)為什么 WebSocket 更好,這不在我們討論的范圍內(nèi),大家可以自行查閱相關(guān)的資料。

今天的內(nèi)容就是簡(jiǎn)單的搭起廣播系統(tǒng)的環(huán)境即可,源碼不多說(shuō)了,因?yàn)閺V播系統(tǒng)實(shí)際上是使用了我們之前學(xué)習(xí)過(guò)的隊(duì)列和事件來(lái)實(shí)現(xiàn)的。而且它也牽涉到一些前端相關(guān)的內(nèi)容,這一塊對(duì)于我來(lái)說(shuō)并沒(méi)有太深度的研究,所以大家看看就好哈。(說(shuō)實(shí)話:實(shí)力不允許啊~~~~)

服務(wù)端配置

默認(rèn)情況下,Laravel 框架中的廣播功能是關(guān)閉的。現(xiàn)在我們需要先去打開(kāi)廣播服務(wù)提供者,它就在 config/app.php 中。

App\Providers\BroadcastServiceProvider::class

將 providers 中的這個(gè)服務(wù)提供者的注釋打開(kāi),我們就可以使用廣播相關(guān)的組件了。然后我們還需要進(jìn)行一些配置。廣播相關(guān)的配置在 config/broadcasting.php 中。

return [

    'default' => env('BROADCAST_DRIVER''null'),

    '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'),
                'useTLS' => true,
            ],
        ],

        'ably' => [
            'driver' => 'ably',
            'key' => env('ABLY_KEY'),
        ],

        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
        ],

        'log' => [
            'driver' => 'log',
        ],

        'null' => [
            'driver' => 'null',
        ],

    ],

];

在這個(gè)配置文件中,我們可以看到有許多不同的廣播連接驅(qū)動(dòng)。pusher 是官方文檔上推薦的,但是,注意這里有但是了哦。這玩意需要去它的官網(wǎng)上注冊(cè)之后拿到 key 了才能使用。而在這們?nèi)粘5氖褂弥?,其?shí)更多的會(huì)使用 redis+socket.io 這種搭配。不過(guò)問(wèn)題就來(lái)了,在 Laravel8 相關(guān)的文檔中,關(guān)于 redis 和 socket.io 的內(nèi)容基本上沒(méi)了。所以我們需要去參考 Laravel6 以及更低版本的文檔。這個(gè)大家在查閱的需要注意哦。

那么接下來(lái)我們就使用 Redis 來(lái)配置,因此,我們需要在 .env 中將 BROADCAST_DRIVER 設(shè)置為 Redis 。

通過(guò)以上的配置,廣播相關(guān)的配置就完成了。接下來(lái)我們需要定義一個(gè)事件,并使用隊(duì)列去消費(fèi)它,前面沒(méi)說(shuō)錯(cuò)吧?廣播在服務(wù)端就是通過(guò)事件和隊(duì)列來(lái)處理的。

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class Messages implements ShouldBroadcast
{
    use DispatchableInteractsWithSocketsSerializesModels;

    public $message;

    public function __construct($newMessage)
    
{
        //
        $this->message = $newMessage;
    }

    public function broadcastOn()
    
{
        return new Channel('messages');
    }
}

我們新定義的這個(gè)事件,需要實(shí)現(xiàn) ShouldBroadcast 接口,然后實(shí)現(xiàn)一個(gè) broadcastOn() 方法。在這個(gè)方法中,返回一個(gè) Channel 實(shí)例,它就是我們要指定廣播的頻道。在這里我們直接給了一個(gè)頻道名稱(chēng)為 messages 。另外,在這個(gè)事件類(lèi)中,我們定義了一個(gè)公共屬性用于接收構(gòu)造函數(shù)傳來(lái)的參數(shù),在廣播事件中,公共屬性是可以廣播到前端去的。

接下來(lái),我們定義一個(gè)路由用于觸發(fā)廣播事件。

Route::get('broadcasting/test'function(){
    broadcast(new \App\Events\Messages("[" . date("Y-m-d H:i:s") . "] 新消息來(lái)了"));
});

在這個(gè)路由中,直接使用 broadcast() 工具函數(shù),傳遞參數(shù)為實(shí)例化的 Messages 事件對(duì)象,給它的構(gòu)造函數(shù)傳遞了一條數(shù)據(jù)。

接下來(lái),我們?cè)L問(wèn)這個(gè)路由,然后到 redis 的隊(duì)列中就可以看到一條數(shù)據(jù)。


看到了吧,事件加隊(duì)列的組合就是這樣的套路,接下來(lái)只需要使用 queue:work 或者 queue:listen 來(lái)監(jiān)聽(tīng)隊(duì)列就可以了。至此,Laravel 框架的服務(wù)端功能我們就完成了。不過(guò),還不是完全完成,因?yàn)槲覀冞€需要一個(gè) laravel-echo-server 組件來(lái)運(yùn)行起一個(gè) socket.io 服務(wù)端。Laravel 隊(duì)列監(jiān)聽(tīng)處理后的內(nèi)容會(huì)到 laravel-echo-server 中,并由 laravel-echo 的服務(wù)端進(jìn)行對(duì)前端的廣播。注意,這個(gè) laravel-echo-server 是一個(gè) npm 工具哦,也就是說(shuō),它是一個(gè) node.js 服務(wù)器。
npm install -g laravel-echo-server

安裝完成后進(jìn)行初始化。

learn-laravel git:(main) ? laravel-echo-server init
Do you want to run this server in development mode? Yes
? Which port would you like to serve from? 6001
? Which database would you like to use to store presence channel membersredis
Enter the host of your Laravel authentication serverhttp://laravel8
Will you be serving on http or httpshttp
Do you want to generate a client ID/Key for HTTP APINo
Do you want to setup cross domain access to the APINo
What do you want this config to be saved aslaravel-echo-server.json
Configuration file savedRun laravel-echo-server start to run server.

在初始化時(shí)選項(xiàng)的內(nèi)容都是很簡(jiǎn)單的英文啦,相信各位大佬的英文水平是沒(méi)問(wèn)題的。然后我們找到在當(dāng)前目錄下生成的 laravel-echo-server.json 文件,修改 devMode 為 ture 。最后運(yùn)行起來(lái)這個(gè)服務(wù)。

learn-laravel git:(main) ? laravel-echo-server start

L A R A V E L  E C H O  S E R V E R

version 1.6.2

? Starting server in DEV mode...

?  Running at localhost on port 6001
?  Channels are ready.
?  Listening for http events...
?  Listening for redis events...

Server ready!

這時(shí),我們運(yùn)行起隊(duì)列監(jiān)控,然后再請(qǐng)求一下廣播路由,會(huì)看到 laravel-echo-server 服務(wù)的命令行下面已經(jīng)對(duì)剛剛的事件進(jìn)行了廣播。

Channel: messages
Event: App\Events\Messages

至此,服務(wù)端的工作全部完成。

客戶端配置

接下來(lái)就是客戶端的配置,也就是我們前端的配置,在進(jìn)行配置前,你需要先安裝相應(yīng)的 npm 庫(kù)。

npm install --save socket.io-client
npm install --save laravel-echo

很明顯,前端對(duì)應(yīng)的是需要一個(gè) socket.io 的客戶端組件和一個(gè) laravel-echo 組件。安裝完成之后,需要去修改一下 resources/assets/js/bootstrap.js 。在這個(gè)文件中,已經(jīng)包含了一套注釋的 Echo 配置,我們需要打開(kāi)注釋并修改成下面這樣。

import Echo from 'laravel-echo';

window.io = require('socket.io-client');

window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001'
    // key: process.env.MIX_PUSHER_APP_KEY,
    // cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    // forceTLS: true
});

注意,注意,注意,重要的事情說(shuō)三遍,現(xiàn)在這里是有坑的哦,我們將在后面解決。大家可以先按這樣修改。

修改完成之后,我們需要使用 Laravel 默認(rèn)的 mix 工具來(lái)編譯一下前端代碼,最后需要加載的文件實(shí)際上是 public/js/app.js ,直接使用下面的命令行進(jìn)行編譯即可。

npm run dev

執(zhí)行完編譯之后,我們就可以寫(xiě)一個(gè)前端頁(yè)面來(lái)進(jìn)行測(cè)試了。在這個(gè)頁(yè)面中,直接引用 app.js 文件即可。

// lresources/views/broadcasting/messages.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>messages</title>
    <meta name="csrf-token" content="{{csrf_token()}}">

    <link href="{{mix('css/app.css')}}" rel="stylesheet">
    <script src="{{mix('js/app.js')}}"></script>

</head>
<body>

<script>
    Echo.channel("messages")
        .listen("Messages", (e)=>{
            console.log(e);
        });
</script>
</body>
</html>

Echo 對(duì)象就是我們?cè)谏厦娴?bootstrap.js 中定義的那個(gè) window.Echo 對(duì)象。在具體的頁(yè)面中,我們直接去調(diào)用它的 channel() 方法,給一個(gè)指定的頻道名稱(chēng),然后監(jiān)聽(tīng)這個(gè)頻道中的具體事件,也就是我們?cè)?Laravel 中定義的事件類(lèi)名。在監(jiān)聽(tīng)的回調(diào)函數(shù)中,我們打印返回的結(jié)果。

最后,定義一個(gè)路由來(lái)顯示這個(gè)頁(yè)面。

Route::view('broadcasting/test2'"broadcasting.messages");

好了,前端相關(guān)的配置也全部完成了?,F(xiàn)在打開(kāi)這個(gè)頁(yè)面吧。

socket.io 問(wèn)題

相信你已經(jīng)打開(kāi)了我們剛剛定義的頁(yè)面,同時(shí)要保證隊(duì)列消費(fèi)和 laravel-echo-server 也正在運(yùn)行,這時(shí)頁(yè)面上會(huì)不停的輪詢一個(gè)類(lèi)似于下面這樣的請(qǐng)求。

http://laravel8:6001/socket.io/?EIO=4&transport=polling&t=NrkU5-3

在你的請(qǐng)求中參數(shù)可能和我的不一樣,但如果看到這個(gè)請(qǐng)求一直在發(fā),并且 console 里沒(méi)有報(bào)錯(cuò)的話,說(shuō)明你的前端配置是沒(méi)有問(wèn)題的。但是,這時(shí)你可以去試試刷新發(fā)送廣播的頁(yè)面,這邊應(yīng)該還是無(wú)法收到推送過(guò)來(lái)的消息。這是為什么呢?

好吧,這個(gè)坑其實(shí)我也找了半天才了解到大概的原因,那就是我們?cè)谏厦嫱ㄟ^(guò) npm 安裝的 socket.io-client 版本太高了。我這里查看 package.json 的話是 4.4 版本的,而 laravel-echo-server 這邊只支持到 2.x 版本。所以我們需要降低版本,最簡(jiǎn)單的方式是注釋掉 bootstrap.js 中引入 socket.io 的那一行。

import Echo from 'laravel-echo';

// window.io = require('socket.io-client');

window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: window.location.hostname + ':6001'
    // key: process.env.MIX_PUSHER_APP_KEY,
    // cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    // forceTLS: true
});

然后在前端頁(yè)面上直接引用一個(gè)低版本的 socket.io 。

<script src="https://cdn./ajax/libs/socket.io/2.3.0/socket.io.js"></script>

接下來(lái)重新編譯 mix 。

npm run dev

現(xiàn)在你再打開(kāi)我們的前端測(cè)試頁(yè)面,就可以看到一個(gè) WebSocket 連接已經(jīng)建立了,之前那個(gè) http 連接也不會(huì)一直輪詢了。這種情況,才是正常的情況。

ws://laravel8:6001/socket.io/?EIO=3&transport=websocket&sid=NTZrvzpCSmX_kuuVAAAB

好了,去刷新一下廣播頁(yè)面發(fā)送廣播吧,然后來(lái)到測(cè)試頁(yè)面看看 Console 中是不是有輸出了。


總結(jié)

開(kāi)心不開(kāi)心,爽不爽,搞了半天總算把這個(gè)廣播系統(tǒng)調(diào)通了吧。相信你的付出一定會(huì)帶來(lái)收獲。整個(gè)廣播系統(tǒng)非常復(fù)雜,僅在后端就有事件、隊(duì)列的應(yīng)用,而且還開(kāi)了一個(gè) node.js 服務(wù)。而在前端還要注意 socket.io 的版本問(wèn)題。具體的源碼我也就不分析了,畢竟僅對(duì)于 Laravel 框架來(lái)說(shuō),無(wú)非就是事件和隊(duì)列的組合應(yīng)用。而前端的實(shí)力確實(shí)還達(dá)不到分析庫(kù)源碼的級(jí)別,所以這里也就不獻(xiàn)丑了。

如果你的系統(tǒng)中有類(lèi)似的通知需求,完全可以考慮使用這套廣播系統(tǒng)來(lái)實(shí)現(xiàn)了,多少還是比輪詢的功能要強(qiáng)大許多,大家多多嘗試就能體會(huì)到好處。最后我再引用一張某位大佬畫(huà)的廣播系統(tǒng)的關(guān)系圖。


參考文檔:

https:///docs/laravel/8.5/broadcasting/10382

https:///docs/laravel/6.x/broadcasting/5159

https://blog.csdn.net/nsrainbow/article/details/80428769

https:///laravel/t/52388

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多