Laravel Broadcasting广播机制(Redis + socket.io)-学习实例

5 篇文章 0 订阅
5 篇文章 0 订阅

借助Laravel Broadcasting你可以使用上时下很热的Websocket技术。

注意:请务必使用较新版本的 Laravel。Laravel在最近几个版本进行过比较大的重构,比如路由从 app\Http\routes.php 拆分为到 routes 目录下的多个文件,包括广播在内的各个附加组件也都进行了重构并正式写入文档。所以网上有些教程(特别是入门教程)可能是根据旧版本来写的,容易让你迷惑。当安装完Laravel后执行以下命令查看Laravel版本

php artisan --version

如果低于 5.4 请重新建立新版本的Laravel,具体方法在下面会详细讲解。

 

实例背景:

假设场景:

假设我们要使用laravel作为服务端做一个新闻推送系统。用户打开页面后不需要刷新页面即可不断的获取到最新的新闻。

环境参数

  • Homestead 7.4.2
  • Laravel 5.6.23 (广播机制在 5.4 以后进行了一次重构,并正式加入文档,所以请务必使用 5.4 及其以上版本)
  • PHP 7.1
  • Redis 4.0.9 (Homestead 自带redis)

建立基本项目框架

使用laravel new 来建立 quickstart 项目

laravel new quickstart

 

我安装完后的laravel版本为5.6.23

 
  1. $ php artisan --version

  2. 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 这个网站

 
  1. - map: quickstart

  2. to: /home/vagrant/Code/quickstart/public

  3. php: "7.1"

3 reload  vagrant 虚拟机

 
  1. cd ~/Homestead

  2. vagrant reload --provision

最后,打开浏览器访问 http://quickstart 你将会看到如下页面

说明项目搭建成功。接下来我们在这个quickstart的基础上为项目添加广播机制。

广播架构

目前有两种广播机制可选:

  • pusher:laravel自带方案,但是有使用限制,需要收费
  • Redis + socket.io:无限制

我们使用业界较流行的Redis + socket.io 方案 。

接下来你会信息爆炸似的接收到好几个新名词:

  • 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订阅服务的订阅者。

这几样东西配合起来的架构图如下

根据这幅图我们可以知道事件的广播机制流程:

  1. Laravel 通过 broadcasting 机制发布一个Event对象到Redis
  2. Laravel Queue Worker 读取该Event对象,并使用Redis的Sub/Pub机制将该 Event对象发布出去
  3. laravel-echo-server  通过 Redis 的 Sub/Pub机制收听到该 Event
  4. 由于 laravel-echo 使用 socket.io 跟 laravel-echo-server相连接。所以 laravel-echo 会通过socket.io将Event对象发送给laravel-echo
  5. laravel-echo解析通过 socket.io接收到的 Event对象

广播事件种类:

  • public:谁都可以收听的广播
  • private:只有指定用户可以收到的广播
  • presence:不仅可以收听到跟你有关的广播,还可以跟别的用户互动,适合做聊天室

我们假设的场景是新闻推送系统,所以使用最简单的public广播就可以实现。接下来我们详细的来讲解如何将这些组件配置好,并连接起来。

我们按照广播机制的流程来一步一步的设置广播机制。

一步步设置广播机制

让Laravel将Event发布到Redis

建立广播服务

注册 BroadcastServiceProvider,打开 config/app.php 找到 'provides' 属性,将 BroadcastServiceProvider 前的注释去掉

 
  1. App\Providers\AppServiceProvider::class,

  2. App\Providers\AuthServiceProvider::class,

  3. App\Providers\BroadcastServiceProvider::class,

  4. App\Providers\EventServiceProvider::class,

  5. App\Providers\RouteServiceProvider::class,

 

设置广播路由

打开广播路由配置文件 routes/channels.php 。该文件用来确定广播事件的流向,在presence广播中还肩负着补充事件内容的工作。我们在 channels.php 中增加一个新的广播通道 news

 
  1. <?php

  2.  
  3. /*

  4. |--------------------------------------------------------------------------

  5. | Broadcast Channels

  6. |--------------------------------------------------------------------------

  7. |

  8. | Here you may register all of the event broadcasting channels that your

  9. | application supports. The given channel authorization callbacks are

  10. | used to check if an authenticated user can listen to the channel.

  11. |

  12. */

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

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

  16. });

  17.  
  18. Broadcast::channel('news', function ($user, $id) {

  19. return true;

  20. });

 

该channel永远返回true意味着无论收听者是谁,他都会收听到最新的广播。

设置Redis连接

由于广播机制是基于queue机制实现的。所以queue的存储设置会直接决定广播事件的存储位置。编辑 .env 文件,修改 QUEUE_DRIVER redis

QUEUE_DRIVER=redis

建立Event

使用以下命令建立 News Event对象

php artisan make:event News

你会发现 app 目录下多出来一个 Events目录,在该目录下产生了广播事件类 News.php。我们要对自动生成的News类进行以下修改:

 

  • 增加对 ShouldBroadcast 的实现
  • 修改broadcastOn 方法,使用公共广播通道 news
  • 修改构造函数
 
  1. class News implements ShouldBroadcast

  2. {

  3. use Dispatchable, InteractsWithSockets, SerializesModels;

  4.  
  5. public $message;

  6. /**

  7. * Create a new event instance.

  8. *

  9. * @return void

  10. */

  11. public function __construct($news_message)

  12. {

  13. $this->message = $news_message;

  14. }

  15.  
  16. /**

  17. * Get the channels the event should broadcast on.

  18. *

  19. * @return \Illuminate\Broadcasting\Channel|array

  20. */

  21. public function broadcastOn()

  22. {

  23. return new Channel('news');

  24. }

  25. }

 

测试广播

接下来,我们要通过新增一个artisan命令来测试是否可以将广播发送到 Redis中。

为项目增加 predis依赖,在项目根目录下执行:

composer require predis/predis

执行完后会发现 composer.json 中增加了 predis 的依赖

 
  1. "require": {

  2. "php": "^7.1.3",

  3. "fideloper/proxy": "^4.0",

  4. "laravel/framework": "5.6.*",

  5. "laravel/tinker": "^1.0",

  6. "predis/predis": "^1.1"

  7. },

编辑 routes/console.php ,增加 bignews 命令:

 
  1. Artisan::command('bignews', function () {

  2. broadcast(new News(date('Y-m-d h:i:s A').": BIG NEWS!"));

  3. $this->comment("news sent");

  4. })->describe('Send news');

执行 bignews 命令:

 
  1. $ php artisan bignews

  2. news sent

通过 redis-cli 查看当前redis中的数据,发现多出来一个queue对象

 
  1. 127.0.0.1:6379> keys *

  2. 1) "queues:default"

Laravel的广播机制成功的连接上了 Redis!

接下来我们让Laravel Queue Worker来消费这些Event

让Laravel Queue Worker消费Event

新开一个终端窗口,并在quickstart根目录下启动 Laravel Queue Worker

$ php artisan queue:work

回到之前的终端窗口,再广播一个news Event:

$ php artisan bignews

你可以在queue worker的执行界面看到该Event已经被检测到,并通过Redis Sub/Pub机制传播出去了

 
  1. vagrant@homestead:~/Code/quickstart$ php artisan queue:work

  2. [2018-05-25 23:24:03][OvuviSKDpM9R5LrIilBEgSLcDpk3hDXc] Processing: App\Events\News

  3. [2018-05-25 23:24:03][OvuviSKDpM9R5LrIilBEgSLcDpk3hDXc] Processed: App\Events\News

还记得之前通过Laravel广播事件之后redis中会产生一个 queue:default对象么?这次如果你快速的通过 keys * 命令查询redis中的对象,你会发现 queue:default 每次被产生出来就会迅速的被 queue worker消费掉

 
  1. 127.0.0.1:6379> keys *

  2. 1) "queues:default"

  3. 127.0.0.1:6379> keys *

  4. (empty list or set)

Laravel Queue Worker连接成功!

接下来我们来让 laravel-echo-server 订阅Redis Sub

让 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,所有问题都使用默认配置:

 
  1. $ laravel-echo-server init

  2. ? Do you want to run this server in development mode? No

  3. ? Which port would you like to serve from? 6001

  4. ? Which database would you like to use to store presence channel members? redis

  5. ? Enter the host of your Laravel authentication server. http://localhost

  6. ? Will you be serving on http or https? http

  7. ? Do you want to generate a client ID/Key for HTTP API? No

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

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

它会帮你在项目根目录下生成 laravel-echo-server.json 配置文件,默认配置为

 
  1. {

  2. "authHost": "http://localhost",

  3. "authEndpoint": "/broadcasting/auth",

  4. "clients": [],

  5. "database": "redis",

  6. "databaseConfig": {

  7. "redis": {},

  8. "sqlite": {

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

  10. }

  11. },

  12. "devMode": false,

  13. "host": null,

  14. "port": "6001",

  15. "protocol": "http",

  16. "socketio": {},

  17. "sslCertPath": "",

  18. "sslKeyPath": "",

  19. "sslCertChainPath": "",

  20. "sslPassphrase": "",

  21. "apiOriginAllow": {

  22. "allowCors": false,

  23. "allowOrigin": "",

  24. "allowMethods": "",

  25. "allowHeaders": ""

  26. }

  27. }

我们来将 devMode 修改为 true

启动

执行以下命令启动 laravel-echo-server

$ laravel-echo-server start

成功启动后会输出以下日志

 
  1. vagrant@homestead:~/Code/quickstart$ laravel-echo-server start

  2.  
  3. L A R A V E L E C H O S E R V E R

  4.  
  5. version 1.3.6

  6.  
  7. ⚠ Starting server in DEV mode...

  8.  
  9. ✔ Running at localhost on port 6001

  10. ✔ Channels are ready.

  11. ✔ Listening for http events...

  12. ✔ Listening for redis events...

  13.  
  14. Server ready!

我们再回到第一个终端窗口,再次广播一个news Event。你会发现 laravel-echo-server 会输出以下日志

 
  1. Channel: news

  2. Event: App\Events\News

  3. CHANNEL news

laravel-echo-server连接成功!

因为laravel-echo-server使用的是 6001 端口,所以记得去 Homestead.yaml里面添加6001 端口的映射

 
  1. - send: 6001

  2. to: 6001

添加完后记得reload vagrant!

接下来,我们要做一个测试页面,让前端js通过socket.io连接上laravel-echo-server。

让laravel-echo收听到广播

安装

由于前端使用的是 laravel-echo来收听广播,我们选择的底层实现方式是socket.io。所以首先我们要在package.json中添加 laravel-echo 和 socket.io的依赖

 
  1. $ npm i --save socket.io-client

  2. $ npm i --save laravel-echo

成功安装后输出以下日志

 
  1. vagrant@homestead:~/Code/quickstart$ npm i --save socket.io-client

  2. npm notice created a lockfile as package-lock.json. You should commit this file.

  3. + socket.io-client@2.1.1

  4. added 30 packages from 23 contributors in 2.401s

  5. [+] no known vulnerabilities found [50 packages audited]

  6.  
  7. vagrant@homestead:~/Code/quickstart$ npm i --save laravel-echo

  8. + laravel-echo@1.3.5

  9. added 1 package from 1 contributor in 1.083s

  10. [+] no known vulnerabilities found [51 packages audited]

  11.  
  12.  
  13.  
  14. ╭────────────────────────────────────────────────────────────────╮

  15. │ │

  16. │ New minor version of npm available! 6.0.0 → 6.1.0 │

  17. │ Changelog: https://github.com/npm/npm/releases/tag/v6.1.0: │

  18. │ Run npm install -g npm to update! │

  19. │ │

  20. ╰────────────────────────────────────────────────────────────────╯

测试页面

js调用

打开 /resources/assets/js/bootstrap.js 你会发现在这个文件的结尾已经预先写上了 laravel-echo 的使用例子

 
  1. /**

  2. * Echo exposes an expressive API for subscribing to channels and listening

  3. * for events that are broadcast by Laravel. Echo and event broadcasting

  4. * allows your team to easily build robust real-time web applications.

  5. */

  6.  
  7. // import Echo from 'laravel-echo'

  8.  
  9. // window.Pusher = require('pusher-js');

  10.  
  11. // window.Echo = new Echo({

  12. // broadcaster: 'pusher',

  13. // key: process.env.MIX_PUSHER_APP_KEY,

  14. // cluster: process.env.MIX_PUSHER_APP_CLUSTER,

  15. // encrypted: true

  16. // });

 

我们只需要将注释去掉,然后修改为使用 socket.io

 
  1. import Echo from 'laravel-echo'

  2. window.io = require('socket.io-client');

  3.  
  4. window.Echo = new Echo({

  5. broadcaster: 'socket.io',

  6. host: window.location.hostname + ':6001'

  7. });

这样页面在初始化的时候就会连接上laravel-echo-server。

bootstrap.js 会被 resources/assets/js/app.js 调用。app.js又通过以下引用在页面被调用:

<script src="{{ mix('js/app.js') }}"></script>

 

创建页面

现在到了创建页面的时候啦!在 resources/views/ 下建立页面 newsroom.blade.php 内容为

 
  1. <!doctype html>

  2. <html>

  3. <head>

  4. <meta charset="utf-8">

  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">

  6. <meta name="viewport" content="width=device-width, initial-scale=1">

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

  8. <title>News Room</title>

  9. <link href="{{ mix('css/app.css') }}" rel="stylesheet">

  10. </head>

  11. <body>

  12. <div class="content">

  13. News Room

  14. </div>

  15. <script src="{{ mix('js/app.js') }}"></script>

  16. <script>

  17. Echo.channel('news')

  18. .listen('News', (e) => {

  19. console.log(e.message);

  20. });

  21. </script>

  22. </body>

  23. </html>

js代码的意思是收听news通道内的News事件对象,将接收到的事件在控制台打印出来。

添加页面路由

在 routes/web.php中增加newsroom的路由信息:

 
  1. <?php

  2.  
  3. /*

  4. |--------------------------------------------------------------------------

  5. | Web Routes

  6. |--------------------------------------------------------------------------

  7. |

  8. | Here is where you can register web routes for your application. These

  9. | routes are loaded by the RouteServiceProvider within a group which

  10. | contains the "web" middleware group. Now create something great!

  11. |

  12. */

  13.  
  14. Route::get('/', function () {

  15. return view('welcome');

  16. });

  17.  
  18. Route::view('newsroom', 'newsroom');

基本构建

在打开浏览器访问页面之前,我们先进行一些基本的环境构建。

停止 laravel-echo-server 然后执行:

$ npm install

打包js和css

$ npm run dev

打包成功后会输出以下日志

 
  1. DONE Compiled successfully in 8489ms 00:28:16

  2.  
  3. Asset Size Chunks Chunk Names

  4. /js/app.js 1.6 MB 0 [emitted] [big] /js/app

  5. /css/app.css 196 kB 0, 0 [emitted] /js/app, /js/app

重新启动laravel-echo-server

基本构建完毕要记得重新启动laravel-echo-server哦!这步太重要了,以至于我专门用了一个节来提醒你。

检验成果

终于到最后的一步了!

打开浏览器访问 http://quickstart/newsroom 你会看到以下页面

打开开发人员工具

接下来是见证奇迹的时刻啦!我们回到第一个终端窗口,发送一个bignews广播:

 
  1. vagrant@homestead:~/Code/quickstart$ php artisan bignews

  2. news sent

我们回到测试页面newsroom的控制台,可以看到收到了一条广播:

大功告成!

如果你没有收到这条广播,没有关系。之前的步骤中我都有提供了如何通过输出日志查看事件的流动结果,按照之前的步骤逐步排查究竟是哪步出了问题就行。

 

--------------------- 作者:alexxiyang 来源:CSDN 原文:https://blog.csdn.net/nsrainbow/article/details/80428769?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用Laravel广播系统时,你可以通过私有频道来限制只有经过认证的用户才能订阅和接收广播事件。下面是私有频道认证的流程和详细机制: 1. 配置广播驱动:在Laravel的配置文件(`config/broadcasting.php`)中,设置你所选择的广播驱动,比如使用pusher、redis等。 2. 定义私有频道:在应用程序中,你需要定义私有频道。可以在`routes/channels.php`文件中使用`Broadcast::channel`方法来定义私有频道。例如: ```php Broadcast::channel('private-channel.{userId}', function ($user, $userId) { return $user->id === $userId; }); ``` 上述示例中,`private-channel.{userId}`是私有频道的名称,`{userId}`是一个变量,表示用户ID。在回调函数中,可以根据需要来定义用户对该频道的访问权限。 3. 认证私有频道:当客户端订阅私有频道时,需要进行频道的认证。认证过程通常通过Ajax请求来完成。 - 客户端订阅私有频道:客户端使用Laravel Echo库来订阅私有频道,例如: ```javascript Echo.private('private-channel.' + userId) .listen('PrivateEvent', (data) => { // 处理接收到的数据 }); ``` - 服务器端认证:客户端发送Ajax请求到服务器,请求进行私有频道的认证。请求中通常携带用户的认证信息,如token或cookie。 - 服务器端处理认证请求:在服务器端,你需要创建一个认证路由,处理私有频道的认证请求。可以使用Laravel提供的`Broadcast::auth`方法来定义认证路由。例如: ```php Broadcast::routes(['middleware' => ['auth:sanctum']]); ``` 上述示例中,使用了Laravel Sanctum中间件来进行身份验证。你也可以根据自己的需求选择其他适合的身份验证方式。 4. 认证逻辑:在认证路由处理方法中,你可以根据请求中携带的认证信息,对用户进行身份验证。验证成功后,返回一个认证通过的响应。例如: ```php Broadcast::channel('private-channel.{userId}', function ($user, $userId) { return $user->id === $userId; }); Broadcast::routes(['middleware' => ['auth:sanctum']]); Route::post('/broadcasting/auth', function (Illuminate\Http\Request $request) { $channelName = $request->input('channel_name'); $userId = $request->user()->id; if (! starts_with($channelName, 'private-channel.')) { return response()->json([], 403); } $channelNameSegments = explode('.', $channelName); $requestedUserId = end($channelNameSegments); if ($userId != $requestedUserId) { return response()->json([], 403); } $auth = $request->user()->createToken('Laravel Broadcasting')->plainTextToken; return response()->json(['auth' => $auth]); }); ``` 上述示例中,首先对请求的频道名称进行验证,确保只有私有频道才能进行认证。然后,根据频道名称中的用户ID和请求中的用户ID进行比较,以确保只有对应的用户才能认证通过。 认证通过后,可以生成一个用于广播系统认证的token,并返回给客户端。 5. 客户端认证:客户端接收到服务器返回的认证token后,使用Laravel Echo的`privateChannel`和`auth`方法进行频道认证。例如: ```javascript Echo.private('private-channel.' + userId) .auth(authToken) .listen('PrivateEvent', (data) => { // 处理接收到的数据 }); ``` 上述示例中,`authToken`是服务器返回的认证token。 通过以上流程和机制,私有频道的认证可以确保只有经过身份验证的用户才能订阅和接收广播事件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值