Laravel构建即时应用的一种实现方法详解
即时交互的应用
大家应该都有所体会,在现代的Web应用中很多场景都需要运用到即时通讯,比如说最常见的支付回调,与三方登录。这些业务场景都基本需要遵循以下流程:
- 客户端触发相关业务,并产生第三方应用的操作(比如支付)
- 客户端等待服务端响应结果(用户完成第三方应用的操作)
- 第三方应用通知服务端处理结果(支付完成)
- 服务端通知客户端处理结果
- 客户端依据结果做出反馈(跳转到支付成功页面)
在过去,为了实现这种即时通讯,能让客户端正确响应处理结果,最为常用的技术就是轮询,因为HTTP协议的单向性,客户端只能一遍一遍的主动询问服务端的处理结果。这种方式有显见的缺陷,占用服务端资源不说,还不能实时获得服务端处理结果。
现在,我们可以使用WebSocket协议来处理实时交互,它是一种双向协议,允许服务端主动推送信息到客户端。本篇我们将借助Laravel强大的事件系统来构建实时的交互。你将需要用到以下知识:
- LaravelEvent
- 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服务的端口,请保持配置文件中的端口一致。
LaravelEvent
这里我们需要借助Laravel强大的事件广播能力:
广播事件
很多现代化的应用中,会使用WebSockets来实现实时交互的用户接口。当一些数据在服务端变更时,一条消息会通过WebSocket连接来传递到客户端进行处理。
为了帮助你构建这种类型的应用。Laravel使通过WebSocket连接进行广播事件变的非常简单。Laravel允许你广播事件来共享事件的名称到你的服务端和客户端的JavaScript框架。
配置
所有的事件广播配置选项都被存储在config/broadcasting.php配置文件中。Laravel附带了几种可用的驱动如Pusher,Redis,和Log,我们将使用Redis作为广播驱动,这里需要依赖predis/predis类库。
由于默认的广播驱动使用的是pusher,所以我们需要在.env文件中设置BROADCAST_DRIVER=redis。
我们创建一个WechatLoginedEvent事件类用来在用户扫描微信登录后进行广播:
token=$token; $this->channel=$channel; } /** *Getthechannelstheeventshouldbebroadcaston. * *@returnarray */ publicfunctionbroadcastOn() { return[$this->channel]; } /** *Getthenametheeventshouldbebroadcaston. * *@returnstring */ publicfunctionbroadcastAs() { return'wechat.login'; } }
其中你需要注意broadcastOn方法应返回一个数组,它表示所需广播的频道,而broadcastAs返回的是一个字符串,它表示广播所触发的事件,Laravel默认的是返回事件类的全类名,这里是App\Events\WechatLoginedEvent.
最重要的是你需要手动的让该类实现ShouldBroadcast契约。Laravel在生成事件时,已经自动添加了该命名空间,该契约只约束broadcastOn方法。
事件完成接下来就是触发事件了,简单的一行代码就可以:
event(newWechatLoginedEvent($token,$channel));
这个操作会自动的触发事件的执行并将信息广播出去。该广播操作底层借助了redis的订阅和发布机制。
RedisBroadcaster会将事件中的允许公开访问的数据通过给定的频道发布出去。如果你想对公开的数据拥有更多的控制,你可以在事件中添加broadcastWith方法,它应该返回一个数组:
/** *Getthedatatobroadcast. * *@returnarray */ publicfunctionbroadcastWith() { return['user'=>$this->user->id]; }
Node.js和Socket.io
对于发布出去的信息,我们需要一个服务来对接,让其能对redis的发布能够进行订阅,并且能把信息以WebSocket协议转发出去,这里我们可以借用Node.js和socket.io来非常方便的构建这个服务:
//server.js varapp=require('http').createServer(handler); vario=require('socket.io')(app); varRedis=require('ioredis'); varredis=newRedis(); app.listen(6001,function(){ console.log('Serverisrunning!'); }); functionhandler(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('userdisconnect') }) }); 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 letio=require('socket.io-client') varsocket=io(':6001') socket.on($channel+':wechat.login',(data)=>{ socket.close() //saveusertokenandredirecttodashboard })
至此整个通讯闭环结束,开发流程看起来就是这样的:
- 在Laravel中构建一个支持广播通知的事件
- 设置需要进行广播的频道及事件名称
- 将广播设置为使用redis驱动
- 提供一个持续的服务用于订阅redis的发布,及将发布内容通过WebSocket协议推送到客户端
- 客户端打开服务端WebSocket隧道,并对事件进行订阅,根据指定事件的推送进行响应。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。