借助Laravel Broadcasting你可以使用上时下很热的Websocket技术。
注意:请务必使用较新版本的 Laravel。Laravel在最近几个版本进行过比较大的重构,比如路由从 app\Http\routes.php 拆分为到 routes 目录下的多个文件,包括广播在内的各个附加组件也都进行了重构并正式写入文档。所以网上有些教程(特别是入门教程)可能是根据旧版本来写的,容易让你迷惑。当安装完Laravel后执行以下命令查看Laravel版本
php artisan --version
如果低于 5.4 请重新建立新版本的Laravel,具体方法在下面会详细讲解。
假设我们要使用laravel作为服务端做一个新闻推送系统。用户打开页面后不需要刷新页面即可不断的获取到最新的新闻。
使用laravel new 来建立 quickstart 项目
laravel new quickstart
我安装完后的laravel版本为5.6.23
$ php artisan --version
Laravel Framework version 5.6.23
编辑 .env 文件,修改 APP_URL=http://quickstart
APP_URL=http://quickstart
我们还需要做3件事情才能让quickstart成功的被部署:
1. 在 hosts 文件中增加 quickstart 到 192.168.10.10 (vagrant地址)的域名解析
192.168.10.10 quickstart
2. 编辑Homestead.yaml增加 quickstart 这个网站
- map: quickstart
to: /home/vagrant/Code/quickstart/public
php: "7.1"
3 reload vagrant 虚拟机
cd ~/Homestead
vagrant reload --provision
最后,打开浏览器访问 http://quickstart 你将会看到如下页面
说明项目搭建成功。接下来我们在这个quickstart的基础上为项目添加广播机制。
目前有两种广播机制可选:
接下来你会信息爆炸似的接收到好几个新名词:
这几样东西配合起来的架构图如下
根据这幅图我们可以知道事件的广播机制流程:
广播事件种类:
我们假设的场景是新闻推送系统,所以使用最简单的public广播就可以实现。接下来我们详细的来讲解如何将这些组件配置好,并连接起来。
我们按照广播机制的流程来一步一步的设置广播机制。
注册 BroadcastServiceProvider,打开 config/app.php 找到 'provides' 属性,将 BroadcastServiceProvider 前的注释去掉
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
打开广播路由配置文件 routes/channels.php 。该文件用来确定广播事件的流向,在presence广播中还肩负着补充事件内容的工作。我们在 channels.php 中增加一个新的广播通道 news
id === (int) $id;
});
Broadcast::channel('news', function ($user, $id) {
return true;
});
该channel永远返回true意味着无论收听者是谁,他都会收听到最新的广播。
由于广播机制是基于queue机制实现的。所以queue的存储设置会直接决定广播事件的存储位置。编辑 .env 文件,修改 QUEUE_DRIVER redis
QUEUE_DRIVER=redis
使用以下命令建立 News Event对象
php artisan make:event News
你会发现 app 目录下多出来一个 Events目录,在该目录下产生了广播事件类 News.php。我们要对自动生成的News类进行以下修改:
class News implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($news_message)
{
$this->message = $news_message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('news');
}
}
接下来,我们要通过新增一个artisan命令来测试是否可以将广播发送到 Redis中。
为项目增加 predis依赖,在项目根目录下执行:
composer require predis/predis
执行完后会发现 composer.json 中增加了 predis 的依赖
"require": {
"php": "^7.1.3",
"fideloper/proxy": "^4.0",
"laravel/framework": "5.6.*",
"laravel/tinker": "^1.0",
"predis/predis": "^1.1"
},
编辑 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('Send news');
执行 bignews 命令:
$ php artisan bignews
news sent
通过 redis-cli 查看当前redis中的数据,发现多出来一个queue对象
127.0.0.1:6379> keys *
1) "queues:default"
Laravel的广播机制成功的连接上了 Redis!
接下来我们让Laravel Queue Worker来消费这些Event
新开一个终端窗口,并在quickstart根目录下启动 Laravel Queue Worker
$ php artisan queue:work
回到之前的终端窗口,再广播一个news Event:
$ php artisan bignews
你可以在queue worker的执行界面看到该Event已经被检测到,并通过Redis Sub/Pub机制传播出去了
vagrant@homestead:~/Code/quickstart$ php artisan queue:work
[2018-05-25 23:24:03][OvuviSKDpM9R5LrIilBEgSLcDpk3hDXc] Processing: App\Events\News
[2018-05-25 23:24:03][OvuviSKDpM9R5LrIilBEgSLcDpk3hDXc] Processed: 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连接成功!
接下来我们来让 laravel-echo-server 订阅Redis Sub
如果使用pusher那么直接使只用laravel就可以了,如果使用 Redis + socket.io 则需要使用开源项目 laravel-echo-server 。所以我们现在要使用 laravel-echo-server。
再新开一个终端窗口。由于我们之前已经开了两个终端窗口了,所以这是第三个终端窗口。
先切换到root用户,然后安装laravel-echo-server
npm install -g laravel-echo-server
我们切换到项目根目录下,初始化 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://localhost",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {},
"sqlite": {
"databasePath": "/database/laravel-echo-server.sqlite"
}
},
"devMode": false,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"apiOriginAllow": {
"allowCors": false,
"allowOrigin": "",
"allowMethods": "",
"allowHeaders": ""
}
}
我们来将 devMode 修改为 true
执行以下命令启动 laravel-echo-server
$ laravel-echo-server start
成功启动后会输出以下日志
vagrant@homestead:~/Code/quickstart$ 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!
我们再回到第一个终端窗口,再次广播一个news Event。你会发现 laravel-echo-server 会输出以下日志
Channel: news
Event: App\Events\News
CHANNEL news
laravel-echo-server连接成功!
因为laravel-echo-server使用的是 6001 端口,所以记得去 Homestead.yaml里面添加6001 端口的映射
- send: 6001
to: 6001
添加完后记得reload vagrant!
接下来,我们要做一个测试页面,让前端js通过socket.io连接上laravel-echo-server。
由于前端使用的是 laravel-echo来收听广播,我们选择的底层实现方式是socket.io。所以首先我们要在package.json中添加 laravel-echo 和 socket.io的依赖
$ npm i --save socket.io-client
$ npm i --save laravel-echo
成功安装后输出以下日志
vagrant@homestead:~/Code/quickstart$ npm i --save socket.io-client
npm notice created a lockfile as package-lock.json. You should commit this file.
+ [email protected]
added 30 packages from 23 contributors in 2.401s
[+] no known vulnerabilities found [50 packages audited]
vagrant@homestead:~/Code/quickstart$ npm i --save laravel-echo
+ [email protected]
added 1 package from 1 contributor in 1.083s
[+] no known vulnerabilities found [51 packages audited]
╭────────────────────────────────────────────────────────────────╮
│ │
│ New minor version of npm available! 6.0.0 → 6.1.0 │
│ Changelog: https://github.com/npm/npm/releases/tag/v6.1.0: │
│ Run npm install -g npm to update! │
│ │
╰────────────────────────────────────────────────────────────────╯
打开 /resources/assets/js/bootstrap.js 你会发现在这个文件的结尾已经预先写上了 laravel-echo 的使用例子
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo'
// window.Pusher = require('pusher-js');
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// encrypted: true
// });
我们只需要将注释去掉,然后修改为使用 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'
});
这样页面在初始化的时候就会连接上laravel-echo-server。
bootstrap.js 会被 resources/assets/js/app.js 调用。app.js又通过以下引用在页面被调用:
现在到了创建页面的时候啦!在 resources/views/ 下建立页面 newsroom.blade.php 内容为
News Room
News Room
js代码的意思是收听news通道内的News事件对象,将接收到的事件在控制台打印出来。
在 routes/web.php中增加newsroom的路由信息:
在打开浏览器访问页面之前,我们先进行一些基本的环境构建。
停止 laravel-echo-server 然后执行:
$ npm install
打包js和css
$ npm run dev
打包成功后会输出以下日志
DONE Compiled successfully in 8489ms 00:28:16
Asset Size Chunks Chunk Names
/js/app.js 1.6 MB 0 [emitted] [big] /js/app
/css/app.css 196 kB 0, 0 [emitted] /js/app, /js/app
基本构建完毕要记得重新启动laravel-echo-server哦!这步太重要了,以至于我专门用了一个节来提醒你。
终于到最后的一步了!
打开浏览器访问 http://quickstart/newsroom 你会看到以下页面
打开开发人员工具
接下来是见证奇迹的时刻啦!我们回到第一个终端窗口,发送一个bignews广播:
vagrant@homestead:~/Code/quickstart$ php artisan bignews
news sent
我们回到测试页面newsroom的控制台,可以看到收到了一条广播:
大功告成!
如果你没有收到这条广播,没有关系。之前的步骤中我都有提供了如何通过输出日志查看事件的流动结果,按照之前的步骤逐步排查究竟是哪步出了问题就行。