Laravel構(gòu)建即時(shí)應(yīng)用的一種實(shí)現(xiàn)方法詳解
即時(shí)交互的應(yīng)用
大家應(yīng)該都有所體會(huì),在現(xiàn)代的 Web 應(yīng)用中很多場(chǎng)景都需要運(yùn)用到即時(shí)通訊,比如說(shuō)最常見(jiàn)的支付回調(diào),與三方登錄。這些業(yè)務(wù)場(chǎng)景都基本需要遵循以下流程:
- 客戶端觸發(fā)相關(guān)業(yè)務(wù),并產(chǎn)生第三方應(yīng)用的操作(比如支付)
- 客戶端等待服務(wù)端響應(yīng)結(jié)果(用戶完成第三方應(yīng)用的操作)
- 第三方應(yīng)用通知服務(wù)端處理結(jié)果(支付完成)
- 服務(wù)端通知客戶端處理結(jié)果
- 客戶端依據(jù)結(jié)果做出反饋 (跳轉(zhuǎn)到支付成功頁(yè)面)
在過(guò)去,為了實(shí)現(xiàn)這種即時(shí)通訊,能讓客戶端正確響應(yīng)處理結(jié)果,最為常用的技術(shù)就是輪詢,因?yàn)?HTTP 協(xié)議的單向性,客戶端只能一遍一遍的主動(dòng)詢問(wèn)服務(wù)端的處理結(jié)果。這種方式有顯見(jiàn)的缺陷,占用服務(wù)端資源不說(shuō),還不能實(shí)時(shí)獲得服務(wù)端處理結(jié)果。
現(xiàn)在,我們可以使用 WebSocket 協(xié)議來(lái)處理實(shí)時(shí)交互,它是一種雙向協(xié)議,允許服務(wù)端主動(dòng)推送信息到客戶端。本篇我們將借助 Laravel 強(qiáng)大的事件系統(tǒng)來(lái)構(gòu)建實(shí)時(shí)的交互。你將需要用到以下知識(shí):
- Laravel Event
- Redis
- Socket.io
- Node.js
Redis
在開(kāi)始之前,我們需要開(kāi)啟一個(gè) redis 服務(wù),并在 Laravel 應(yīng)用中進(jìn)行配置啟用,因?yàn)樵谡麄€(gè)流程中,我們需要借助 redis 的訂閱和發(fā)布機(jī)制來(lái)實(shí)現(xiàn)即時(shí)通訊。
Redis 是一個(gè)開(kāi)源高效的鍵值對(duì)存儲(chǔ)系統(tǒng)。它通常作為一個(gè)數(shù)據(jù)結(jié)構(gòu)服務(wù)器來(lái)存儲(chǔ)鍵值對(duì),它可以支持字符串,散列,列表,集合和有序結(jié)合。在 Laravel 中使用 Redis 你需用通過(guò) Composer 來(lái)安裝 predis/predis 包文件。
配置
Redis 在應(yīng)用中的配置文件存儲(chǔ)在 config/database.php,在這個(gè)文件中,你可以看到一個(gè)包含了 Redis 服務(wù)信息的 redis 數(shù)組:
'redis' => [ 'cluster' => false, 'default' => [ 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ], ]
如果你修改了 redis 服務(wù)的端口,請(qǐng)保持配置文件中的端口一致。
Laravel Event
這里我們需要借助 Laravel 強(qiáng)大的事件廣播能力:
廣播事件
很多現(xiàn)代化的應(yīng)用中,會(huì)使用 Web Sockets 來(lái)實(shí)現(xiàn)實(shí)時(shí)交互的用戶接口。當(dāng)一些數(shù)據(jù)在服務(wù)端變更時(shí),一條消息會(huì)通過(guò) WebSocket 連接來(lái)傳遞到客戶端進(jìn)行處理。
為了幫助你構(gòu)建這種類(lèi)型的應(yīng)用。Laravel 使通過(guò) WebSocket 連接進(jìn)行廣播事件變的非常簡(jiǎn)單。Laravel 允許你廣播事件來(lái)共享事件的名稱(chēng)到你的服務(wù)端和客戶端的 JavaScript 框架。
配置
所有的事件廣播配置選項(xiàng)都被存儲(chǔ)在 config/broadcasting.php 配置文件中。Laravel 附帶了幾種可用的驅(qū)動(dòng)如 Pusher,Redis,和 Log,我們將使用 Redis 作為廣播驅(qū)動(dòng),這里需要依賴(lài) predis/predis 類(lèi)庫(kù)。
由于默認(rèn)的廣播驅(qū)動(dòng)使用的是 pusher,所以我們需要在 .env 文件中設(shè)置 BROADCAST_DRIVER=redis
。
我們創(chuàng)建一個(gè) WechatLoginedEvent 事件類(lèi)用來(lái)在用戶掃描微信登錄后進(jìn)行廣播:
<?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 方法應(yīng)返回一個(gè)數(shù)組,它表示所需廣播的頻道,而 broadcastAs 返回的是一個(gè)字符串,它表示廣播所觸發(fā)的事件,Laravel 默認(rèn)的是返回事件類(lèi)的全類(lèi)名,這里是 App\Events\WechatLoginedEvent.
最重要的是你需要手動(dòng)的讓該類(lèi)實(shí)現(xiàn) ShouldBroadcast 契約。Laravel 在生成事件時(shí),已經(jīng)自動(dòng)添加了該命名空間,該契約只約束 broadcastOn 方法。
事件完成接下來(lái)就是觸發(fā)事件了,簡(jiǎn)單的一行代碼就可以:
event(new WechatLoginedEvent($token, $channel));
這個(gè)操作會(huì)自動(dòng)的觸發(fā)事件的執(zhí)行并將信息廣播出去。該廣播操作底層借助了 redis 的訂閱和發(fā)布機(jī)制。
RedisBroadcaster 會(huì)將事件中的允許公開(kāi)訪問(wèn)的數(shù)據(jù)通過(guò)給定的頻道發(fā)布出去。如果你想對(duì)公開(kāi)的數(shù)據(jù)擁有更多的控制,你可以在事件中添加 broadcastWith 方法,它應(yīng)該返回一個(gè)數(shù)組:
/** * Get the data to broadcast. * * @return array */ public function broadcastWith() { return ['user' => $this->user->id]; }
Node.js 和 Socket.io
對(duì)于發(fā)布出去的信息,我們需要一個(gè)服務(wù)來(lái)對(duì)接,讓其能對(duì) redis 的發(fā)布能夠進(jìn)行訂閱,并且能把信息以 WebSocket 協(xié)議轉(zhuǎn)發(fā)出去,這里我們可以借用 Node.js 和 socket.io 來(lái)非常方便的構(gòu)建這個(gè)服務(wù):
// 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 服務(wù)端并監(jiān)聽(tīng) 6001 端口,借用 redis 的 psubscribe 指令使用通配符來(lái)快速的批量訂閱,接著在消息觸發(fā)時(shí)將消息通過(guò) WebSocket 轉(zhuǎn)發(fā)出去。
Socket.io 客戶端
在 web 前端,我們需要引入 Socket.io 客戶端開(kāi)啟與服務(wù)端 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 })
至此整個(gè)通訊閉環(huán)結(jié)束,開(kāi)發(fā)流程看起來(lái)就是這樣的:
- 在 Laravel 中構(gòu)建一個(gè)支持廣播通知的事件
- 設(shè)置需要進(jìn)行廣播的頻道及事件名稱(chēng)
- 將廣播設(shè)置為使用 redis 驅(qū)動(dòng)
- 提供一個(gè)持續(xù)的服務(wù)用于訂閱 redis 的發(fā)布,及將發(fā)布內(nèi)容通過(guò) WebSocket 協(xié)議推送到客戶端
- 客戶端打開(kāi)服務(wù)端 WebSocket 隧道,并對(duì)事件進(jìn)行訂閱,根據(jù)指定事件的推送進(jìn)行響應(yīng)。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
php微信公眾號(hào)開(kāi)發(fā)之關(guān)鍵詞回復(fù)
這篇文章主要為大家詳細(xì)介紹了php微信公眾號(hào)開(kāi)發(fā)之關(guān)鍵詞回復(fù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10Laravel框架實(shí)現(xiàn)redis集群的方法分析
這篇文章主要介紹了Laravel框架實(shí)現(xiàn)redis集群的方法,簡(jiǎn)單分析了Laravel框架redis數(shù)據(jù)庫(kù)集群功能設(shè)置步驟、相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2017-09-09PHP zip擴(kuò)展Linux下安裝過(guò)程分享
這篇文章主要介紹了PHP zip擴(kuò)展Linux下安裝過(guò)程分享,需要的朋友可以參考下2014-05-05在Laravel5中正確設(shè)置文件權(quán)限的方法
這篇文章主要給大家介紹了關(guān)于如何在Laravel5中正確設(shè)置文件權(quán)限的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Laravel5具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05PHP中比較兩個(gè)字符串找出第一個(gè)不同字符位置例子
這是一個(gè)在stackoverflow上的問(wèn)題。 給出兩個(gè)長(zhǎng)度相等的字符串,找出這兩個(gè)字符串中第一個(gè)不同的字符位置。2014-04-04php+jQuery.uploadify實(shí)現(xiàn)文件上傳教程
這篇文章主要介紹了php+jQuery.uploadify實(shí)現(xiàn)文件上傳教程,需要的朋友可以參考下2014-12-12