系列文章目录
8.laravel-echo-server 订阅Redis Sub
前言
在如今的 web 程序中,WebSockets 被用来实现需要实时、即时更新的接口。当服务器上的数据被更新后,更新信息将通过 WebSocket 连接发送到客户端等待处理。相比于页面使用定时器不停地轮询的程序,WebSocket 是一种更加可靠和高效的选择。
Laravel通过Broadcasting可以使用如今时下很热的Websocket技术。 Broadcasting允许你在服务端代码和客户端 JavaScript 应用之间共享相同的事件名。
一、环境参数
- Ubuntu 22.04.1 LTS
- Redis 6.0.16
- PHP 7.4.30
- Laravel 5.6.40 (广播机制在 5.4 以后进行了一次重构,并正式加入文档,所以请务必使用 5.4 及其以上版本)
二、创建项目框架
构建脚手架过程省略...这里主要说明的是使用 laravel-echo demo
三、广播架构
查阅 技术文档 目前有两种广播机制可供选择:
- pusher:laravel自带方案,但是有使用限制,需要收费
- Redis + socket.io:无使用限制
我们使用 第二种 Redis + socket.io 这种方案。
你需要了解如下信息:
1.相关信息标识
- laravel-echo-server:使用 socket.io 机制实现的 broadcasting 服务端
- laravel-echo:laravel-echo是 laravel broadcasting 的客户端。注意,laravel-echo 并不是 laravel-echo-server 专属的客户端, laravel-echo 有两种连接机制可以选:pusher 和 socket.io 。 而 laravel-echo-server 是开发出来专门用于 socket.io连接的服务端。如果你使用的是 pusher,那么不需要使用 laravel-echo-server ,但是你依然要使用 laravel-echo
- Socket.IO:websocket 的一种nodejs实现。laravel-echo 如果要使用socket.io 则需要先安装 socket.io-client。
- Predis:redis客户端的php实现,如果要使用redis作为广播机制的实现,则需要先安装 predis
- Laravel Event:广播事件类
- Laravel Queue:广播机制是基于queue机制来实现的
- Redis Sub/Pub:Redis的订阅机制。laravel-echo-server本质上只是一个Redis订阅服务的订阅者。
2.流程架构图及过程:
- Laravel 通过 broadcasting 机制发布一个Event对象到Redis
- Laravel Queue Worker 读取该Event对象,并使用Redis的Sub/Pub机制将该 Event对象发布出去
- laravel-echo-server 通过 Redis 的 Sub/Pub机制收听到该 Event
- 由于 laravel-echo 使用 socket.io 跟 laravel-echo-server相连接。所以 laravel-echo 会通过socket.io将Event对象发送给laravel-echo
- laravel-echo解析通过 socket.io接收到的 Event对象
3.广播事件种类:
- public:谁都可以收听的广播
- private:只有指定用户可以收到的广播
- presence:不仅可以收听到跟你有关的广播,还可以跟别的用户互动,适合做聊天室
四、demo 示例构建
1.假设场景
假设我们要使用laravel作为服务端做一个新闻推送系统。用户打开页面后不需要刷新页面即可不断的获取到最新的新闻。
2.建立广播服务
注册 BroadcastServiceProvider,打开 config/app.php 找到 'provides' 属性,将 BroadcastServiceProvider 前的注释去掉,如下代码示例(app.php 部分代码):
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
3.设置广播路由
打开广播路由配置文件 routes/channels.php 。该文件用来确定广播事件的流向,在presence广播中还肩负着补充事件内容的工作。我们在 channels.php 中增加一个新的广播通道 news
<?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;
});
// 该channel永远返回true意味着无论收听者是谁,他都会收听到最新的广播。
Broadcast::channel('news_test', function () {
return true;
});
4.设置Redis连接
由于广播机制是基于queue机制实现的。所以queue的存储设置会直接决定广播事件的存储位置。编辑 .env 文件,修改 QUEUE_DRIVER = redis
QUEUE_DRIVER=redis
5.建立Event
php artisan make:event News
项目根目录下的 app 文件夹中会多出来一个 Events目录,该目录下产生了广播事件类 News.php文件。
针对刚生成News类进行如下修改:
- 增加对 ShouldBroadcast 的实现
- 修改broadcastOn 方法,使用公共广播通道 news_test
- 修改构造函数
<?php
namespace App\Events;
use Carbon\Carbon;
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 News implements ShouldBroadcast // 1. 事件是要广播出去的
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($news_message) // 2.广播出去的内容
{
//
$this->message = $news_message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn() // 3.对那些频道进行广播
{
// 创建频道
return new Channel('news_test');
// return new PrivateChannel('channel-name');
}
/**
* 指定广播数据
*/
// public function broadcastWith()
// {
//
// // 返回当前时间
// return ['name' => Carbon::now()->toDateString(),'message'=> $this->message];
//
// }
}
6.测试广播
为项目增加 predis依赖,在项目根目录下执行:
composer require predis/predis
检测执行完后 composer.json 中增加了 predis 的依赖
"require": { "php": "^7.4.3", "darkaonline/l5-swagger": "5.6.*", "fideloper/proxy": "^4.0", "guzzlehttp/guzzle": "^7.4", "laravel/framework": "5.6.*", "laravel/tinker": "^1.0", "predis/predis": "1.1", "spatie/laravel-permission": "~2.7", "ext-json": "*" },
新增一个artisan命令来测试是否可以将广播发送到 Redis中,编辑 routes/console.php ,增加 bignews 命令。
Artisan::command('bignews', function () { broadcast(new News(date('Y-m-d h:i:s A').": BIG NEWS!")); $this->comment("news sent"); })->describe('这是一个news 测试');
执行 bignews 命令:
$ php artisan bignews
$ news sent
通过 redis-cli 查看当前redis中的数据,发现多出来一个queue对象
127.0.0.1:6379> keys *
1) "queues:default"
到此 Laravel的广播机制就成功的连接上了 Redis!
7.Laravel Queue Worker消费Event
新开一个终端窗口,并在根目录下启动 Laravel Queue Worker
php artisan queue:work
之前的终端窗口,再广播一个news Event:
php artisan bignews
你可以在queue worker的执行界面看到该Event已经被检测到,并通过Redis Sub/Pub机制传播出去了
$ php artisan queue:work
$ [2018-05-25 23:24:03][OvuviSKDpM9R5LrIilBEgSLcDpk3hDXc] Processing:
App\Events\News
还记得之前通过Laravel广播事件之后redis中会产生一个 queue:default对象么?这次如果你快速的通过 keys * 命令查询redis中的对象,你会发现 queue:default 每次被产生出来就会迅速的被 queue worker消费掉
-
127.0.0.1:6379> keys *
-
1) "queues:default"
-
127.0.0.1:6379> keys *
-
(empty list or set)
Laravel Queue Worker连接成功!
8.laravel-echo-server 订阅Redis Sub
8.1 安装 laravel-echo-server
如果使用pusher那么直接使只用laravel就可以了,如果使用 Redis + socket.io 则需要使用开源项目 laravel-echo-server 。所以我们现在要使用 laravel-echo-server。
再新开一个终端窗口。由于我们之前已经开了两个终端窗口了,所以这是第三个终端窗口。
先切换到root用户,然后安装laravel-echo-server
npm install -g laravel-echo-server
8.2 初始化
我们切换到项目根目录下,初始化 laravel-echo-server,所有问题都使用默认配置:
$ laravel-echo-server init
? Do you want to run this server in development mode? No
? 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. http://localhost
? Will you be serving on http or https? http
? Do you want to generate a client ID/Key for HTTP API? No
? Do you want to setup cross domain access to the API? No
Configuration file saved. Run laravel-echo-server start to run server.
它会帮你在项目根目录下生成 laravel-echo-server.json 配置文件,默认配置(下面我已经改过)为
{
"authHost": "http://127.0.0.1:8644",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {},
"sqlite": {
"databasePath": "/database/laravel-echo-server.sqlite"
}
},
"devMode": true,
"host": "127.0.0.1",
"port": "6001",
"protocol": "http",
"socketio": {},
"secureOptions": 67108864,
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"subscribers": {
"http": true,
"redis": true
},
"apiOriginAllow": {
"allowCors": false,
"allowOrigin": "",
"allowMethods": "",
"allowHeaders": ""
}
}
8.3 启动
执行以下命令启动 laravel-echo-server
$ laravel-echo-server start
成功启动后会输出以下日志
我们再回到第一个终端窗口,再次广播一个news Event。你会发现 laravel-echo-server 会输出以下日志
laravel-echo-server连接成功!
因为laravel-echo-server使用的是 6001 端口,所以记得去 Nginx 里面添加6001 端口的映射
注意: 小编就是因为这边之前没有走通
在 /etc/nginx/conf.d/8644.conf (该项目配置路径)里面编辑 填加如下代码
...
location ~/socket.io {
proxy_pass http://localhost:6001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
...
添加完后记得重启 nginx
9.让laravel-echo收听到广播
9.1 安装
由于前端使用的是 laravel-echo来收听广播,我们选择的底层实现方式是socket.io。所以首先我们要在package.json中添加 laravel-echo 和 socket.io的依赖
$ npm i --save socket.io-client@^2.2.0
$ npm i --save laravel-echo@^1.5.2
跟 main.js 同级创建 bootstrap.js并编辑
import Echo from 'laravel-echo'
window.io = require('socket.io-client')
window.Echo = new Echo({
broadcaster: 'socket.io',
host: 'http://127.0.0.1:8101'
// auth: {
// headers: {
// Authorization: 'Basic NDAxX0NkcVc6NDAxX0R1YTYza3JS'
// }
// }
})
// // 测试
window.Echo.channel('news_test').listen('News', (e) => {
console.log('1111111111111111')
console.log(e)
})
在 main.js中 引入,如下:
require('./bootstrap')
import Vue from 'vue'
import Cookies from 'js-cookie'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import Element from 'element-ui'
import './styles/element-variables.scss'
...
然后运行前端vue 代码
npm run dev
9.2 最终效果
后端监控
前端接受
总结
以上就是今天要讲的内容,本文仅仅简单介绍了 vue+laravel前后端分离项目中使用laravel-echo的使用,而利用WebSocket 是一种更加可靠和高效的选择。