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 相关文章推荐
真正面向对象编程:PHP5.01发布
Oct 09 PHP
PHP最常用的ini函数分析 针对PHP.ini配置文件
Apr 22 PHP
PHP学习笔记之数组篇
Jun 28 PHP
通过5个php实例细致说明传值与传引用的区别
Aug 08 PHP
file_get_contents获取不到网页内容的解决方法
Mar 07 PHP
ThinkPHP3.1新特性之命名范围的使用
Jun 19 PHP
Parse正式发布开源PHP SDK
Aug 11 PHP
php的sso单点登录实现方法
Jan 08 PHP
PHP实现获取客户端IP并获取IP信息
Mar 17 PHP
PHP计算数组中值的和与乘积的方法(array_sum与array_product函数)
Apr 01 PHP
php简单实现数组分页的方法
Apr 30 PHP
PHP实现的简单在线计算器功能示例
Aug 02 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
PHP中MD5函数使用实例代码
2008/06/07 PHP
php打开文件fopen函数的使用说明
2013/07/05 PHP
php数据类型判断函数有哪些
2013/09/23 PHP
php使用GeoIP库实例
2014/06/27 PHP
php实现将字符串按照指定距离进行分割的方法
2015/03/14 PHP
magento后台无法登录解决办法的两种方法
2016/12/09 PHP
用函数式编程技术编写优美的 JavaScript
2006/11/25 Javascript
js 匿名调用实现代码
2009/06/19 Javascript
广泛收集的jQuery拖放插件集合
2012/04/09 Javascript
JQuery插件Style定制化方法的分析与比较
2012/05/03 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
2013/08/09 Javascript
jQuery中的val()示例应用
2014/02/26 Javascript
JavaScript实现文字跟随鼠标特效
2015/08/06 Javascript
正则表达式优化JSON字符串的技巧
2015/12/24 Javascript
DropDownList控件绑定数据源的三种方法
2016/12/24 Javascript
js实现九宫格的随机颜色跳转
2017/02/19 Javascript
用JS实现简单的登录验证功能
2017/07/28 Javascript
微信小程序Page中data数据操作和函数调用方法
2019/05/08 Javascript
解决layui表格内文本超出隐藏的问题
2019/09/12 Javascript
Vue数组响应式操作及高阶函数使用代码详解
2020/08/01 Javascript
[01:50]2014DOTA2西雅图邀请赛 专访欢乐周宝龙
2014/07/08 DOTA
Python+PIL实现支付宝AR红包
2018/02/09 Python
解决DataFrame排序sort的问题
2018/06/07 Python
关于ZeroMQ 三种模式python3实现方式
2019/12/23 Python
Python 实现网课实时监控自动签到、打卡功能
2020/03/12 Python
Python3将ipa包中的文件按大小排序
2020/04/17 Python
Pytorch通过保存为ONNX模型转TensorRT5的实现
2020/05/25 Python
使用Python操作MySQL的小技巧
2020/09/10 Python
python中round函数保留两位小数的方法
2020/12/04 Python
html5 Canvas画图教程(2)—画直线与设置线条的样式如颜色/端点/交汇点
2013/01/09 HTML / CSS
HTML5 manifest离线缓存的示例代码
2018/08/08 HTML / CSS
iPhoneX安全区域(Safe Area)底部小黑条在微信小程序和H5的屏幕适配
2020/04/08 HTML / CSS
全球烹饪课程的领先预订平台:Cookly
2020/01/28 全球购物
值班管理制度范本
2015/08/06 职场文书
OpenCV实现普通阈值
2021/11/17 Java/Android
日本官方排名前10的动漫,名侦探柯南上榜,第一是一部创造历史的动漫
2022/03/18 日漫