基于 Redis 实现 Laravel 广播功能(中):引入 Laravel Echo 接收广播消息
启动 Laravel Echo Server
上篇教程我们完成了广播系统的后端配置和事件分发,并探究了底层源码的实现,最终落地的都是通过 Redis 发布命令发布消息。
接下来我们需要借助 Laravel Echo Server 搭建起 Websocket 服务器,这里面除了封装 Socket.io 服务端之外,还包含了订阅服务端广播频道的 Redis 客户端,用于接收服务端 Redis 发布的消息,再通过 Socket.io 广播给客户端。
如果是在本地搭建,按照 Laravel Echo Server 文档给出的安装和启动步骤操作即可,如果使用的是 Laradock,其内置了 laravel-echo-server
这个容器服务配置,使用 docker-compose up -d laravel-echo-server
启动即可,如果使用的是 Laravel Sail 作为本地开发环境,可以参考 Laradock 提供的 laravel-echo-server
编排配置引入它。
通过 Sail 编排 Laravel Echo Server
在项目根目录的 docker
目录下(我已经通过 sail artisan sail:publish
发布了 Sail 的容器编排文件,所有会有这个目录)新建一个 laravel-echo-server
子目录,然后把 laradock/laravel-echo-server
目录下的所有文件拷贝到这个子目录下:
修改 laravel-echo-server.json
配置如下:
{
"authHost": "redis.test",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {
"port": "6379",
"host": "redis"
}
},
"devMode": true,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"sslCertPath": "",
"sslKeyPath": "",
"subscribers": {
"http": true,
"redis": true
},
"apiOriginAllow": {
"allowCors": true,
"allowOrigin": "redis.test",
"allowMethods": "GET, POST",
"allowHeaders": "Origin, Content-Type, X-Auth-Token, X-Requested-With, Accept, Authorization, X-CSRF-TOKEN, X-Socket-Id"
}
}
在项目根目录下 docker-compose.yml
的 services
中新增一个 laravel-echo-server
服务编排配置:
services:
...
laravel-echo-server:
build:
context: ./docker/laravel-echo-server
dockerfile: Dockerfile
args:
- CHANGE_SOURCE=${CHANGE_SOURCE}
volumes:
- ./docker/laravel-echo-server/laravel-echo-server.json:/app/laravel-echo-server.json:ro
ports:
- "${LARAVEL_ECHO_SERVER_PORT}:6001"
depends_on:
- redis
networks:
- sail
在 .env
中新增两个配置项:
CHANGE_SOURCE=true
LARAVEL_ECHO_SERVER_PORT=6001
然后就可以通过如下命令启动 Laravel Echo Server 容器服务了:
sail up -d
初次构建会先拉取 laravel-echo-server
的容器镜像。启动完成后,就可以通过 sail ps
命令查看它是否启动成功:
或者通过查看 laravel-echo-server
日志也可以确认它是否启动成功:
Laravel Echo 客户端
启动好 Laravel Echo Server 后,接下来,我们来安装配置 Laravel 官方提供的广播客户端前端库 Laravel Echo,它既支持 Pusher,也支持 Socket.io,这里我们肯定需要通过 Socket.io 客户端进行通信了。
由于我们上篇教程已经在项目中安装过 socket.io-client
,所以只需要单独安装 laravel-echo
即可,不过需要把 package.json
中已安装的 socket.io-client
版本调整为与 laravel-echo-server
中的 socket.io
版本一致,否则很可能导致 Websocket 连接建立失败(学院君就遇到了这个问题,折腾了半天,网上也没啥靠谱的答案,最后灵感突发,猜测是不是客户端与服务端版本不一致引起的,最后验证了下还真是,目前这个版本号是 2.3.0
,将 socket.io-client
版本号调整为 ^2.3.0
即可):
npm install --save laravel-echo
然后在 resources/js/bootstrap.js
中取消 Laravel Echo 相关代码前面的注释,并将 Pusher 客户端实现调整为 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'
});
window.Echo.channel('laravel_database_test-channel').listen('UserSignedUp', event => {
console.log(event.user);
});
运行 sail npm run dev
编译前端代码让上述修改生效。
至此,我们就将前面基于 Redis + Socket.io 原生代码实现的事件广播功能重构为了基于 Laravel 广播组件 + Laravel Echo Server + Laravel Echo 实现的完整广播系统了,这样一来,我们就可以使用 Laravel 广播系统提供的所有功能了,包括事件广播的推送和接收、私有频道、存在频道等。
不过在此之前,我们还是验证下这个广播系统是否可以正常工作。
验证 Laravel 事件广播消息推送
在访问 /broadcast
路由前,还需要在 resources/views/websocket.blade.php
的 <head>
标签中添加获取 CSRF 令牌的代码以便被 Laravel Echo 读取:
<meta name="csrf-token" content="{{ csrf_token() }}">
在浏览器中访问 http://redis.test/broadcast
,此时服务端还没有推送事件广播消息,但是可以在 laravel-echo-server
日志中看到 Websocket 客户端信息,joined
表示客户端与服务端建立连接,left
表示客户端断开连接,之所以出现下面这个 joined-left-joined
日志,是因为我刷新过 /broadcast
页面:
在浏览器中也可以在开发者工具中看到熟悉 Socket.io Websocket 连接成功消息流:
接下来,我们运行如下 Artisan 命令分发事件广播:
sail artisan redis:publish
然后启动队列处理器进程处理 broadcast
队列消息(这一步也不能漏了哈,因为 Laravel 默认是基于消息队列处理广播消息的):
sail artisan queue:work --queue=broadcast
这个时候,查看 laravel-echo-server
的日志,就可以看到服务端发布的事件消息已经被 Laravel Echo Server 中的 Redis 接收处理了:
底层原理和我们通过 Redis + Socket.io 原生代码实现广播功能是一样的,基于 Redis 订阅功能实现,感兴趣的同学可以去看下 Laravel Echo Server 实现源码。
在浏览器页面开发者工具的 Console
中,也可以看到客户端接已经收到这个事件消息并打印出用户信息来了:
至此,从 Laravel 服务端到 Laravel Echo Server 到 Laravel Echo 客户端的广播链路就已经打通了。
8 Comments
laravel-echo-server.json 文件中authHost 需要和docker-compose.yml 中的APP容器名称一致
是的 其实就是和 Laravel 后端应用域名保持一致
我想知道在windows平台本地环境下在laravel-echo-server.json这个文件中"database": ""的值,我写127.0.0.1报错Database driver not set.,写redis也是报错[ioredis] Unhandled error event: Error: read ECONNRESET at TCP.onStreamRead (internal/stream_base_commons.js:209:20),经过goolge百度查找以为是node版本问题,可是换了最新的也不行,普通开发环境该写什么啊?菜鸟表示不理解
跟 node 没关系 ,database 肯定是 redis,databaseConfig 里面的 host 改成 127.0.0.1 了吗
我的配置这样就会报错,Database driver not set, "database": "redis", "databaseConfig": { "redis": { "port": "6379", "host": "127.0.0.1" }, "publishPresence": true, "sqlite": { "databasePath": "/database/laravel-echo-server.sqlite" } },
配置 sqlite 干嘛
我的浏览器里network里每两三秒就请求一次状态都是200,返回了一条看不懂的数据,就是这条:96:0{"sid":"XRqh9S8uz7pmlYskAAA4","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40 里面的sid是变化的后面的不变,浏览器里请求的网址是::6001/socket.io/?EIO=4&transport=polling&t=NR_0PfQ
sqlite是看github上的配置加上去的,不过加不加都是一样的结果,反正这个配置没有用到