laravel廣播

概述

在laravel中優雅地使用長鏈接的方式。
服務端只需要廣播一個事件,客戶端就可以收到該事件的廣播。
總體就是:服務端事件->socket->瀏覽器。

其中服務端就用laravel的event事件和廣播相關功能完成。
其中socket部分由依賴包laravel-echo-server服務完成。
其中瀏覽器也就是前端,由依賴包socket.io-client和laravel-echo完成。

最後達到的效果就是:在laravel程序中需要廣播給client端的東西寫成事件,並在業務中觸發。
客戶端js部分,訂閱對應頻道,以及對應服務端的事件名,然後處理收到的該事件的內容。
這樣,就可以很方便的在laravel中開發長鏈接相關功能!

初始化配置

這裏使用的框架版本爲5.8,將使用redis作爲廣播的驅動,所以請在你本機運行redis(或者有遠程redis地址也可以)。

  1. 默認laravel關閉了廣播服務,需要在config/app.php文件中取消註釋。

    App\Providers\BroadcastServiceProvider::class
    
  2. laravel默認給redis驅動設置了前綴,需要在config/database.php文件中註釋掉。

    // 'options' => [
    //     'cluster' => env('REDIS_CLUSTER', 'predis'),
    //     'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
    // ],
    
  3. 修改.env配置文件,將廣播驅動設置爲redis。

    BROADCAST_DRIVER=redis
    
  4. 安裝依賴

    如果首次運行npm,需要先運行npm install安裝laravel相關npm依賴。
    如果npm下載慢可以用阿里的鏡像http://npm.taobao.org/,安裝阿里的cnpm後使用cnpm安裝。

    安裝負責監聽處理socket的nodejs包:
    npm install -g laravel-echo-server

    安裝前端的事件處理包:
    npm install --save socket.io-client
    npm install --save laravel-echo

    安裝predis
    composer require predis/predis

  5. 運行socket服務
    laravel-echo-server init // 首次運行請執行該初始化命令,會在項目根目錄生成laravel-echo-server.json配置文件

    laravel-echo-server start // 啓動

公共頻道例子

執行命令創建一個事件處理器:php artisan make:event TestEvent
編輯事件類,實現ShouldBroadcast接口類:implements ShouldBroadcast。

<?php

namespace App\Events;

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

class TestEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

	public $message;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($test)
    {
        //
        $this->message = $test;
    }

    // 廣播頻道
    public function broadcastOn()
    {
        // return new PrivateChannel('channel-name');
        return new Channel('test_public_channel');
    }
    
    // 廣播內容
    public function broadcastWith()
    {
        return [
            'data' => $this->message
        ];
    }

    // 廣播事件名稱,默認爲 App\Events\TestEvent
    public function broadcastAs()
    {
        return 'TestEvent';
    }

    // 決定是否廣播的條件
    public function broadcastWhen()
    {
        if ('test') {
            return true;
        }
    }
}

在前端收聽廣播:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>
</head>
<body>
<script type="module">
    import Echo from '/static/echo/echo.js';
	
	// 連接socket服務
    window.Echo = new Echo({
        broadcaster: 'socket.io',
        host: 'http://'+window.location.hostname + ':6001'
    });
	
	// 訂閱公共頻道並監聽事件
    window.Echo.channel('test_public_channel')
    .listen('.TestEvent', (e) => { // 注意,如果在laravel事件中用broadcastAs方法設置了廣播事件名稱,則這裏監聽的事件名稱前要加"."。
        console.log(e);
    });
</script>
</body>
</html>

通過瀏覽器訪問該視圖,可見其成功連接了laravel-echo-server服務:
在這裏插入圖片描述
通過laravel-echo-server的控制檯也可以看到加入了頻道:在這裏插入圖片描述
然後在業務中觸發該事件,由該事件向頻道里發送廣播,客戶端收到廣播後在console中打印出廣播內容:

	某某Controller:
    public function test(Request $request)
    {
        broadcast(new \App\Events\TestEvent("123321"));
    }

然後通過瀏覽器或cli,訪問這個方法,訪問後在瀏覽器可見收到了socket消息:
在這裏插入圖片描述
在這裏插入圖片描述
如果服務端推送時想放到隊列中非阻塞執行,也很簡單,只需要在TestEvent事件類中指定事件使用哪個隊列:

public $broadcastQueue = 'TestBroadcastQueue';

隊列驅動在config/queue.php中設置,我這裏使用的redis,然後在cli運行隊列:

php artisan queue:work --queue=TestBroadcastQueue

再次觸發TestEvent廣播事件,通過隊列發送效果:
在這裏插入圖片描述
如果想定時發送,那就在業務中不直接調用事件進行廣播,而是添加到job並設定延時,由job執行時調用事件進行廣播。

私有頻道例子

私有頻道需要用戶被授權之後才能監聽。
客戶端向服務端發起一個監聽請求,由服務端決定該用戶是否可監聽該頻道。
接下來我們創建一個消息盒子,服務端發送給指定用戶的消息實時推送給指定用戶。

由於我這邊是新框架沒有用戶登錄代碼,所以先初始化一下:
php artisan migrate
php artisan make:auth
然後瀏覽器訪問/home註冊一個用戶。

接下來在路由中增加私有頻道路由,處理用戶監聽請求的授權判斷:

routes/channels.php:

<?php

/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/

Broadcast::channel('App.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

Broadcast::channel('message-box-{id}', function ($user, $userid) {
    return (int) $user->id === (int) $userid;
});

創建一個事件廣播:
php artisan make:event MessageBoxEvent

<?php

namespace App\Events;

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

class MessageBoxEvent implements ShouldBroadcastNow
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;
    
    public $userid;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($userid, $test)
    {
        $this->userid = $userid;
        $this->message = $test;
    }

    // 廣播頻道
    public function broadcastOn()
    {
        return new PrivateChannel('message-box-'.$this->userid);
    }
    
    // 廣播內容
    public function broadcastWith()
    {
        return [
            'data' => $this->message
        ];
    }

    // 廣播事件名稱,默認爲 App\Events\MessageBoxEvent
    public function broadcastAs()
    {
        return 'MessageBoxEvent';
    }

    // 決定是否廣播的條件
    public function broadcastWhen()
    {
        if ('test') {
            return true;
        }
    }
}

視圖增加監聽私有頻道代碼:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="/static/jquery/jquery-1.10.1.min.js"></script>
    <script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>
</head>
<body>
    
<script type="module">
    import Echo from '/static/echo/echo.js';
	
	// 連接socket服務
    window.Echo = new Echo({
        broadcaster: 'socket.io',
        host: 'http://' + window.location.hostname + ':6001'
    });
	
	// 訂閱公共頻道並監聽事件
    window.Echo.channel('test_public_channel')
    .listen('.TestEvent', (e) => { // 注意,如果在laravel事件中用broadcastAs方法設置了廣播事件名稱,則這裏監聽的事件名稱前要加"."。
        console.log(e);
    });

    // 訂閱私有頻道並監聽事件
    window.Echo.private('message-box-{{ Auth::user()->id}}')
    .listen('.MessageBoxEvent', (e) => { // 注意,如果在laravel事件中用broadcastAs方法設置了廣播事件名稱,則這裏監聽的事件名稱前要加"."。
        console.log(e);
    });

</script>
</body>
</html>

然後某Controller中的某方法調用該事件:

broadcast(new \App\Events\MessageBoxEvent(\Auth::user()->id,"測試發送給單獨用戶的消息"));

瀏覽器訪問觸發後效果:
在這裏插入圖片描述
laravel-echo-server控制檯輸出:
在這裏插入圖片描述
參考資料:
https://learnku.com/laravel/t/13101/using-laravel-echo-server-to-build-real-time-applications
https://learnku.com/index.php/docs/laravel/5.8/broadcasting/3914

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章