用laravel-echo-server实现实时消息通知

用laravel-echo-server实现实时消息通知

一、实时消息

举个栗子,如聊天消息和点赞评论通知等,都是通过laravel提供的消息通知系统,将相关的notification对象存储到数据库中,然后提供一些api或者gql接口给前端,然后前端不停地轮询接口,获得更新数据。这样会有两个显而易见的问题:

  1. 消息没有更新的时候,前端值轮询接口导致了很多无效的接口查询,增加服务器压力,影响app加载速度。
  2. 消息有更新,但是轮询会有一定的时间间隔,就会导致消息更新不及时。

这个缺陷是由于HTTP协议的天然缺陷导致的:请求只能从客户端发起。因此使用HTTP协议通讯是无法让服务端无法及时通知客户端数据更新的,这里就涉及了一个新的协议。WebSocket。

WebSocket它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。由此可见,相比于不停地轮询应用程序,WebSocket 是一种更加可靠和高效的选择。

而laravel的广播系统,就支持使用WebSocket来搭建自己的实时消息系统。

二、Laravel Echo Server

WebSocket 服务器的实现,常用的 Node 实现有以下三种。

而开源项目tlaverdure/laravel-echo-server,是一个与 Laravel 兼容的 Socket.IO 服务器。

GitHub:https://github.com/tlaverdure/laravel-echo-server

下图解释了laravel怎么通过socket.io来完成了一个消息通知。

在这里插入图片描述

三、搭建一个实时消息通知系统

Laravel 上的实时消息通知时基于事件系统广播系统,在这之前,可以先去复习一下这两块的知识点。

1、设置广播驱动

Laravel的广播系统自带了4个广播驱动器,pusher,redis,log,null

这里我们使用redis作为驱动,先用下面的命令安装redis拓展支持。

composer require  predis/predis "~1.0"

修改.env文件:BROADCAST_DRIVER=redis

如果是未曾使用广播系统的项目,需要先到config/app.php 配置文件的 providers 数组中取消对广播服务的提供者BroadcastServiceProvider的注释.

2、服务端,配置laravel-echo-server

安装方法
npm install -g laravel-echo-server  # 这里是全局安装
初始化服务端

根据自己的需要填充配置参数。

$ 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 members? redis

? Enter the host of your Laravel authentication server. your_host_address

? Will you be serving on http or https? http

? Do you want to generate a client ID/Key for HTTP API? Yes

? Do you want to setup cross domain access to the API? No

appId: c953434932b06864

key: 551440289d2d41c81e87d55c1d0217e5

Configuration file saved. Run laravel-echo-server start to run server.

初始化完成之后,项目下就会生成一个 laravel-echo-sever.json的配置文件,里面的配置就是我们在初始化时设置的值。后续的服务都是基于这个配置文件启动的。

本项目用到的配置为:

{

    "authHost": "http://xxx.xxx.cn",

    "authEndpoint": "/api/broadcasting/auth",

    "clients": [],

    "database": "redis",

    "databaseConfig": {

        "redis": {

            "port": "port",

            "host": "host",

            "db": "db",

            "password": "password"

        },

        "sqlite": {

            "databasePath": "/database/laravel-echo-server.sqlite"

        }

    },

    "devMode": true,

    "host": null,

    "port": "6002",

    "protocol": "http",

    "socketio": {},

    "secureOptions": 67108864,

    "sslCertPath": "",

    "sslKeyPath": "",

    "sslCertChainPath": "",

    "sslPassphrase": "",

    "subscribers": {

        "http": true,

        "redis": true

    },

    "apiOriginAllow": {

        "allowCors": false,

        "allowOrigin": "",

        "allowMethods": "",

        "allowHeaders": ""

    }

}
运行服务端
$ laravel-echo-server start



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



version 1.3.6



⚠ Starting server in DEV mode...



✔  Running at localhost on port 6001

✔  Channels are ready.

✔  Listening for http events...

✔  Listening for redis events...



Server ready!

检测下socket.io客户端url地址是否可访问

http://your_host_address:6001/socket.io/socket.io.js

可访问,并内容类似于以下则说明服务器可用

!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(......

3、广播自己的通知

这里以系统通知Notice为例,Notice为系统通知实体,包含了通知内容content,通知对象to_user_id等。

业务场景是每当运营创建一条系统消息时,用户可以及时接受到相关的通知。这里就分为了个人通知和系统通知两部分。对应为公共频道广播和私人频道广播。

定义广播事件类NewNotice
<?php



namespace App\Events;



use App\Notice;

use Illuminate\Broadcasting\Channel;

use Illuminate\Broadcasting\InteractsWithSockets;

use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

use Illuminate\Foundation\Events\Dispatchable;

use Illuminate\Support\Str;



class NewNotice implements ShouldBroadcast

{

    use Dispatchable, InteractsWithSockets;



    public $notice;



    /**

     * Create a new event instance.

     *

     * @return void

     */

    public function __construct(Notice $notice)

    {

        $this->notice = $notice;

    }



    /**

     * 广播该事件到哪一个频道,channel代表一个公共频道

     * 私人频道使用PrivateChannel

     */

    public function broadcastOn()

    {

        return new Channel('notice');

    }

    //广播事件名

    public function broadcastAs()

    {

        return 'system.notice';

    }

    //广播消息携带的内容数据

    public function broadcastWith()

    {

        $notice = $this->notice;

        return [

            'title' => $notice->title,

            'description' => Str::limit($notice->content, 20),

            'content' => $notice->content,

            'id' => $notice->id,

        ];

    }

}
对事件进行广播
class NoticeObserver

{

    /**

     * Handle the notice "created" event.

     *

     * @param  \App\Notice  $notice

     * @return void

     */

    public function created(Notice $notice)

    {

        broadcast(new NewNotice($notice));            

     }

}

开启队列监听:

php artisan queue:work

以上这些就可以将我们的广播发布到事件发布到指定频道,这里是将systen.notice广播事件广播到了公共频道notice.

laravel-echo-server会打印相应的日志:

Channel: notice

Event: system.notice
广播到私人频道

发送给某个人的系统通知,是只有指定的接收方才能接收到的,那么就需要监听一个特定的私人频道,并进行用户的授权校验。

      public function broadcastOn()

    {

        return new PrivateChannel('App.User.' . $this->user->id);

    }

私人频道可以拼接用户id作为用户名,保证每个用户都能监听一个属于自己的私人频道,该用户的所有私人广播事件也都可以广播到这一个频道上。

  • 频道授权

对于私有频道,用户只有被授权后才能监听。实现过程是用户向你的 Laravel 应用程序发起一个携带频道名称的 HTTP 请求,你的应用程序判断该用户是否能够监听该频道。在使用 Laravel Echo 时,上述 HTTP 请求会被自动发送;尽管如此,你仍然需要定义适当的路由来响应这些请求。

  • 授权路由:

BroadcastServiceProvider 中,你会看到一个对 Broadcast::routes 方法的调用。该方法会注册 /broadcasting/auth 路由来处理授权请求(与配置文件中的authEndpoint对应):

  • 授权回调

接下来,我们需要定义真正用于处理频道授权的逻辑。这是在 routes/channels.php 文件中完成。在该文件中,你可以用 Broadcast::channel 方法来注册频道授权回调函数:

Broadcast::channel('App.User.{id}', function ($user, $id) {

    return (int) $user->id === (int) $id;

});

channel 方法接收两个参数:频道名称和一个回调函数,该回调通过返回 true 或 false 来表示用户是否被授权监听该频道。

u s e r , 是 当 前 被 认 证 的 用 户 , 任 何 额 外 的 通 配 符 参 数 作 为 后 续 参 数 。 这 里 的 user,是当前被认证的用户,任何额外的通配符参数作为后续参数。这里的 userid,也就是我们在定义名称是传递的$this->user->id.

4、客户端监听

安装 Laravel Echo

Laravel Echo 是一个 JavaScript 库,它使得订阅频道和监听由 Laravel 广播的事件变得非常容易。你可以通过 NPM 包管理器来安装 Echo。在本例中,因为我们将使用 Pusher 广播器,请安装 pusher-js 包:

npm install --save laravel-echo pusher-js

一旦 Echo 被安装好,你就可以在你应用程序的 JavaScript 中创建一个全新的 Echo 实例。做这件事的一个理想地方是在 resources/assets/js/bootstrap.js 文件的底部,Laravel 框架自带了该文件:

import Echo from "laravel-echo"



window.Echo = new Echo({

     broadcaster: 'socket.io',

     host: 'ws://xxx.xxx.cn:6002',

});

ws是webSocket协议标识符,如果加密,则为wss,服务器网址就是 URL,对应于http和https。

对事件进行监听

一旦你安装好并实例化了 Echo,你就可以开始监听事件广播了。首先,使用 channel 方法来获取一个频道实例,然后调用 listen 方法来监听指定的事件:

Echo.channel('notice')

    .listen('.system.notice', (e) => {

        console.log(e);

    });
  • 要注意,如果您使用 broadcastAs 方法自定义广播名称,你需要在客户端使用订阅事件的时候为事件类加上 . 前缀

  • 如果采用的是默认的类名作为广播事件名,那么要记得检查database配置中是否有添加默认的prefix

如果你想监听私有频道上的事件,请使用 private 方法。你可以通过链式调用 listen 方法来监听一个频道上的多个事件:

Echo.private('App.User.'+me.id)

    .listen(...)

    .listen(...)

    .listen(...);
退出频道

如果想退出频道,你需要在你的 Echo 实例上调用 leave 方法:

Echo.leave('orders');
生产环境配置

本地我们是自己执行laravel-echo-server start来启动服务的,生产环境我们采用supervisor来自动管理laravel-echo-server服务。

[program:laravel-worker-yinxiangshipin-echo-server]

process_name=%(program_name)s_%(process_num)02d

directory=/xxx/.../yinxiangshipin.com

command=/xxx/.../.nvm/versions/node/v10.16.0/bin/node /xxx/.../.nvm/versions/node/v10.16.0/bin/laravel-echo-server start --dir=/xxx/.../yinxiangshipin.com

autostart=true

autorestart=true

user=root

numprocs=1

redirect_stderr=true

stdout_logfile=/xxx/.../yinxiangshipin.com/storage/logs/echo-server.log

supervisor相关配置可以查看相关文档,这里我们需要安装的东西为nvm,node,laravel-echo-server

Nvm is Node Version Manager github:nvm-sh

  • 安装nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash\n

或者

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash
  • 添加对应环境变量
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

然后使用nvm安装最新的node

nvm install node

或者安装指定版本

nvm install v10.16.0

然后安装 laravel-echo-server

npm install -g laravel-echo-server

安装完成后默认的目录为~/.nvm/versions/node/v10.15.2/bin/laravel-echo-server

验证安装成功,

执行laravel-echo-server.json中配置的command,能执行这两个命令那么就是全部安装成功了

如果提示没有node命令,那么就需要自己手动添加一个软链接

ln -s /xxx/…/.nvm/versions/node/v10.16.0/bin/node /usr/bin/node

完成上述操作之后,就可以restart laravel-echo-server队列,验证服务是否可以成功启动了。

supervisorctl restart laravel-worker-yinxiangshipin-echo-server:

如果启动失败,可以检查一下启动用户是否有权限

到这里,已经完成了一个简单的实时消息系统啦,如果还有问题,可以查看stdout_logfile配置的log路径,检查执行日志,排查问题。

如果对laravel 关于这部分的实现有兴趣,相应看看源码的话,可以参考一下这篇文章
看源代码,解析一次完整的 public channel 下发流程:https://learnku.com/articles/17327

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值