使用laravel广播推送消息

项目是基于laravel6开发的一个论坛项目,里面有公告和私信功能,需求要求做到实时推送


方案
  1. 使用php socket 搭建
  2. 使用laravel自带的广播

php socket在laravel中使用搭建起来比较麻烦,而且要封装函数,对于一个小功能来说成本太高,决定使用laravel自带的广播功能来实现

laravel广播驱动类型
  1. pusher
  1. 官方默认的,收费
  1. redis
  1. 可搭配socket.io使用
最终方案

使用laravel广播基于redis驱动搭配socket.io实现该功能

因为要使用node.js安装相关包,要先安装node.js

  1. 打开laravel默认关闭的广播服务 config\app.php

laravel 消息队列通知 laravel消息通知广播_redis

  1. 注释掉redis配置里的前缀设置(laravel中默认有前缀) config'database.php

laravel 消息队列通知 laravel消息通知广播_laravel_02

  1. 修改.env文件中的修改广播驱动为redis

laravel 消息队列通知 laravel消息通知广播_laravel 消息队列通知_03

4.安装laravel中redis扩展,这里以predis为列

composer require predis/predis

安装完成后设置.env中的REDIS_CLIENT设置为predis 如果不想设置,可以调整config\database.php中的clinet默认为predis

laravel 消息队列通知 laravel消息通知广播_php_04

  1. 安装依赖
// 更新依赖

npm install

//安装负责处理socket的node.js包

npm install -g laravel-echo-server

//安装前端事务处理包

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

//运行socket服务

laravel-echo-server init

//启动

laravel-echo-server start

运行成功

laravel 消息队列通知 laravel消息通知广播_redis_05

  1. 查看npm安装包

laravel 消息队列通知 laravel消息通知广播_laravel 消息队列通知_06

  1. 创建一个event php artisan make:event TestEvent 代码如下
<?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;
        }
    }
}
  1. 前端接收广播
//需要加入token 广播强制要求

<meta name="csrf-token" content="{{ csrf_token() }}">

//引入socket.io.js

<script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>


<script type="module">
//在js中引入echo.js

 import Echo from '/static/echo.js';
 
 //初始化echo
 
 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>

注:如果找不到echo.js文件,在项目下node_modules\laravel-echo\dist找到echo.js复制到对应的目录下即可

  1. 调用方法
broadcast(new TestEvent('你的文章被点赞了'));

前端收到对应消息

laravel 消息队列通知 laravel消息通知广播_开发语言_07

  1. 私有频道

推送消息是时候用到了私有频道,操作如下:

  1. 创建一个私有频道的event php artisan make:event PrivateEvent ,代码如下:
<?php

namespace App\Events;

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

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

    protected $user_id;
    protected $info;
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($user_id,$info)
    {
        $this->user_id =$user_id;
        $this->info = $info;

    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('user-'.$this->user_id);
    }


    public function broadcastAs()
    {
        return 'PrivateEvent';

    }

    public function broadcastWith()
    {
        $data = ['id'=>$this->user_id,'type'=>$this->info['type'],'message'=>$this->info['message']];
        return ['data'=>$data];
    }
}

2.在路由channel.php中设置如下内容

//这里的意思只有前端用户才有权限

Broadcast::channel('user-.{user_id}', function ($user_id, $data) {
    return true;
},['guards'=>'web']);

3.前端代码

//需要加入token 广播强制要求

<meta name="csrf-token" content="{{ csrf_token() }}">

//引入socket.io.js

<script src="//{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>


<script type="module">
//在js中引入echo.js

 import Echo from '/static/echo.js';
 
 //初始化echo
 
 window.Echo = new Echo({
        broadcaster: 'socket.io',
        host: 'http://' + window.location.hostname + ':6001'
    });
 
  //调用私有频道监控
  
    window.Echo.private("user-{{auth()->id()}}")
        .listen('.PrivateEvent', (e) => {// 注意,如果在laravel事件中用broadcastAs方法设置了广播事件名称,则这里监听的事件名称前要加"."。
            console.log(e);
        })
        
 </script>