Laravel构建即时应用的一种实现方法详解


Posted in PHP onAugust 31, 2017

即时交互的应用

大家应该都有所体会,在现代的 Web 应用中很多场景都需要运用到即时通讯,比如说最常见的支付回调,与三方登录。这些业务场景都基本需要遵循以下流程:

  • 客户端触发相关业务,并产生第三方应用的操作(比如支付)
  • 客户端等待服务端响应结果(用户完成第三方应用的操作)
  • 第三方应用通知服务端处理结果(支付完成)
  • 服务端通知客户端处理结果
  • 客户端依据结果做出反馈 (跳转到支付成功页面)

在过去,为了实现这种即时通讯,能让客户端正确响应处理结果,最为常用的技术就是轮询,因为 HTTP 协议的单向性,客户端只能一遍一遍的主动询问服务端的处理结果。这种方式有显见的缺陷,占用服务端资源不说,还不能实时获得服务端处理结果。

现在,我们可以使用 WebSocket 协议来处理实时交互,它是一种双向协议,允许服务端主动推送信息到客户端。本篇我们将借助 Laravel 强大的事件系统来构建实时的交互。你将需要用到以下知识:

  • Laravel Event
  • Redis
  • Socket.io
  • Node.js

Redis

在开始之前,我们需要开启一个 redis 服务,并在 Laravel 应用中进行配置启用,因为在整个流程中,我们需要借助 redis 的订阅和发布机制来实现即时通讯。

Redis 是一个开源高效的键值对存储系统。它通常作为一个数据结构服务器来存储键值对,它可以支持字符串,散列,列表,集合和有序结合。在 Laravel 中使用 Redis 你需用通过 Composer 来安装 predis/predis 包文件。

配置

Redis 在应用中的配置文件存储在 config/database.php,在这个文件中,你可以看到一个包含了 Redis 服务信息的 redis 数组:

'redis' => [
 'cluster' => false,

 'default' => [
 'host' => '127.0.0.1',
 'port' => 6379,
 'database' => 0,
 ],
]

如果你修改了 redis 服务的端口,请保持配置文件中的端口一致。

Laravel Event

这里我们需要借助 Laravel 强大的事件广播能力:

广播事件

很多现代化的应用中,会使用 Web Sockets 来实现实时交互的用户接口。当一些数据在服务端变更时,一条消息会通过 WebSocket 连接来传递到客户端进行处理。

为了帮助你构建这种类型的应用。Laravel 使通过 WebSocket 连接进行广播事件变的非常简单。Laravel 允许你广播事件来共享事件的名称到你的服务端和客户端的 JavaScript 框架。

配置

所有的事件广播配置选项都被存储在 config/broadcasting.php 配置文件中。Laravel 附带了几种可用的驱动如 Pusher,Redis,和 Log,我们将使用 Redis 作为广播驱动,这里需要依赖 predis/predis 类库。

由于默认的广播驱动使用的是 pusher,所以我们需要在 .env 文件中设置 BROADCAST_DRIVER=redis

我们创建一个 WechatLoginedEvent 事件类用来在用户扫描微信登录后进行广播:

<?php

namespace App\Events;

use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class WechatLoginedEvent extends Event implements ShouldBroadcast
{
 use SerializesModels;

 public $token;
 protected $channel;

 /**
  * Create a new event instance.
  *
  * @param string $token
  * @param string $channel
  * @return void
  */
 public function __construct($token, $channel)
 {
  $this->token = $token;
  $this->channel = $channel;
 }

 /**
  * Get the channels the event should be broadcast on.
  *
  * @return array
  */
 public function broadcastOn()
 {
  return [$this->channel];
 }

 /**
  * Get the name the event should be broadcast on.
  *
  * @return string
  */
 public function broadcastAs()
 {
  return 'wechat.login';
 }
}

其中你需要注意 broadcastOn 方法应返回一个数组,它表示所需广播的频道,而 broadcastAs 返回的是一个字符串,它表示广播所触发的事件,Laravel 默认的是返回事件类的全类名,这里是 App\Events\WechatLoginedEvent.

最重要的是你需要手动的让该类实现 ShouldBroadcast 契约。Laravel 在生成事件时,已经自动添加了该命名空间,该契约只约束 broadcastOn 方法。

事件完成接下来就是触发事件了,简单的一行代码就可以:

event(new WechatLoginedEvent($token, $channel));

这个操作会自动的触发事件的执行并将信息广播出去。该广播操作底层借助了 redis 的订阅和发布机制。

RedisBroadcaster 会将事件中的允许公开访问的数据通过给定的频道发布出去。如果你想对公开的数据拥有更多的控制,你可以在事件中添加 broadcastWith 方法,它应该返回一个数组:

/**
 * Get the data to broadcast.
 *
 * @return array
 */
 public function broadcastWith() 
 {
 return ['user' => $this->user->id];
 }

Node.js 和 Socket.io

对于发布出去的信息,我们需要一个服务来对接,让其能对 redis 的发布能够进行订阅,并且能把信息以 WebSocket 协议转发出去,这里我们可以借用 Node.js 和 socket.io 来非常方便的构建这个服务:

// server.js
var app = require('http').createServer(handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function () {
 console.log('Server is running!') ;
});

function handler(req, res) {
 res.writeHead(200);
 res.end('');
}

io.on('connection', function (socket) {
 socket.on('message', function (message) {
 console.log(message)
 })
 socket.on('disconnect', function () {
 console.log('user disconnect')
 })
});


redis.psubscribe('*', function (err, count) {
});

redis.on('pmessage', function (subscrbed, channel, message) {
 message = JSON.parse(message);
 io.emit(channel + ':' + message.event, message.data);
});

这里我们使用 Node.js 引入 socket.io 服务端并监听 6001 端口,借用 redis 的 psubscribe 指令使用通配符来快速的批量订阅,接着在消息触发时将消息通过 WebSocket 转发出去。

Socket.io 客户端

在 web 前端,我们需要引入 Socket.io 客户端开启与服务端 6001 端口的通讯,并订阅频道事件:

// client.js
let io = require('socket.io-client')

var socket = io(':6001')
  socket.on($channel + ':wechat.login', (data) => {
  socket.close()
  // save user token and redirect to dashboard
})

至此整个通讯闭环结束,开发流程看起来就是这样的:

  • 在 Laravel 中构建一个支持广播通知的事件
  • 设置需要进行广播的频道及事件名称
  • 将广播设置为使用 redis 驱动
  • 提供一个持续的服务用于订阅 redis 的发布,及将发布内容通过 WebSocket 协议推送到客户端
  • 客户端打开服务端 WebSocket 隧道,并对事件进行订阅,根据指定事件的推送进行响应。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
php 引用(&amp;)详解
Nov 20 PHP
PHP动态分页函数,PHP开发分页必备啦
Nov 07 PHP
php生成excel文件的简单方法
Feb 08 PHP
php数组保存文本与文本反编成数组实例
Nov 13 PHP
PHP中调用SVN命令更新网站方法
Jan 07 PHP
php通过ksort()函数给关联数组按照键排序的方法
Mar 18 PHP
php获得刚插入数据的id 的几种方法总结
May 31 PHP
PHP正则验证字符串是否为数字的两种方法并附常用正则
Feb 27 PHP
详解提高使用Java反射的效率方法
Apr 29 PHP
tp5.1 框架数据库-数据集操作实例分析
May 26 PHP
基于PHP实现解密或加密Cloudflar邮箱保护
Jun 24 PHP
php命令行模式代码实例详解
Feb 26 PHP
[原创]PHP实现字节数Byte转换为KB、MB、GB、TB的方法
Aug 31 #PHP
PHP下 Mongodb 连接远程数据库的实例代码
Aug 30 #PHP
php实现与python进行socket通信的方法示例
Aug 30 #PHP
利用PHP访问MySql数据库的逻辑操作以及增删改查的实例讲解
Aug 30 #PHP
PHP Socket网络操作类定义与用法示例
Aug 30 #PHP
[原创]php token使用与验证示例【测试可用】
Aug 30 #PHP
Laravel框架中VerifyCsrfToken报错问题的解决
Aug 30 #PHP
You might like
Windows下XDebug 手工配置与使用说明
2010/07/11 PHP
phpmyadmin安装时提示:Warning: require_once(./libraries/common.inc.php)错误解决办法
2011/08/18 PHP
PHP解密Unicode及Escape加密字符串
2015/05/17 PHP
PHP 根据key 给二维数组分组
2016/12/09 PHP
使用PHP+MySql+Ajax+jQuery实现省市区三级联动功能示例
2017/09/15 PHP
jquery 最简单易用的表单验证插件
2010/02/27 Javascript
jQuery学习笔记之jQuery的DOM操作
2010/12/22 Javascript
仅IE支持clearAttributes/mergeAttributes方法使用介绍
2012/05/04 Javascript
JS 毫秒转时间示例代码
2013/09/22 Javascript
jquery的map与get方法详解
2013/11/04 Javascript
网页右下角弹出窗体实现代码
2014/06/05 Javascript
node.js中的buffer.Buffer.isBuffer方法使用说明
2014/12/14 Javascript
JavaScript实现的字符串replaceAll函数代码分享
2015/04/02 Javascript
谈谈JavaScript中function多重理解
2015/08/28 Javascript
三种带箭头提示框总结实例
2016/06/14 Javascript
在Web项目中引入Jquery插件报错的完美解决方案(图解)
2016/09/19 Javascript
JavaScript实现通过select标签跳转网页的方法
2016/09/29 Javascript
微信小程序 WebSocket详解及应用
2017/01/21 Javascript
jQuery中绑定事件bind() on() live() one()的异同
2017/02/23 Javascript
理解Koa2中的async&amp;await的用法
2018/02/05 Javascript
Vue指令v-for遍历输出JavaScript数组及json对象的常见方式小结
2019/02/11 Javascript
初试vue-cli使用HBuilderx打包app的坑
2019/07/17 Javascript
javascript设计模式 ? 策略模式原理与用法实例分析
2020/04/21 Javascript
jQuery zTree如何改变指定节点文本样式
2020/10/16 jQuery
详解Django通用视图中的函数包装
2015/07/21 Python
python中常用的九种预处理方法分享
2016/09/11 Python
Python数据结构与算法之图的广度优先与深度优先搜索算法示例
2017/12/14 Python
PyTorch的深度学习入门之PyTorch安装和配置
2019/06/27 Python
Python 实现自动完成A4标签排版打印功能
2020/04/09 Python
Python文件夹批处理操作代码实例
2020/07/21 Python
夜大毕业生自我评价分享
2013/11/10 职场文书
音乐教学随笔感言
2014/02/19 职场文书
高等教育学专业自荐书
2014/06/17 职场文书
领导班子四风对照检查材料思想汇报
2014/09/26 职场文书
全面盘点MySQL中的那些重要日志文件
2021/11/27 MySQL
MySQL之MyISAM存储引擎的非聚簇索引详解
2022/03/03 MySQL