本文是使用laraval的event與node.js做爲websocket服務器,與頁面實現長鏈接;php
基本知識前端
配置node
1. Laravel 中使用 Redis 你需用經過 Composer 來安裝 predis/predis
包文件。web
2.Redis 在應用中的配置文件存儲在 config/database.php
,在這個文件中,你能夠看到一個包含了 Redis 服務信息的 redis
數組:redis
'redis' => [ 'cluster' => false, 'default' => [ 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ], ]
3.Laravel Event數組
Laravel 經過廣播事件來共享事件到的服務端和客戶端的 JavaScript 框架。服務器
全部的事件廣播配置選項都被存儲在 config/broadcasting.php
配置文件中。Laravel 附帶了幾種可用的驅動如 Pusher,Redis,和 Log,咱們將使用 Redis 做爲廣播驅動,這裏須要依賴 predis/predis
類庫。websocket
因爲默認的廣播驅動使用的是 pusher,因此咱們須要在 .env
文件中設置 BROADCAST_DRIVER=redis
。cookie
下面是一個事件類的例子app
<?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class MessageEvent implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; public $messages; protected $channel; /** * Create a new event instance. * * @return void */ public function __construct($channel, $messages) { $this->channel = $channel; $this->messages = $messages; } /** * Get the channels the event should broadcast on. * * @return Channel|array */ public function broadcastOn() { return [$this->channel]; } public function broadcastAs() { return 'OnPushMessage'; }
public function broadcastWith() { return $this->messages; } }
注意:
broadcastOn方法應返回一個數組,它表示所需廣播的頻道
broadcastAs返回的是一個字符串,它表示廣播所觸發的事件
broadcastWith 返回的是一個數組,它表示要給給定頻道發佈過去的數據
接下來是觸發事件:
event(new MessageEvent($message->channel_id, $send_message));
這個操做會自動的觸發事件的執行並將信息廣播出去。該廣播操做底層藉助了 redis 的訂閱和發佈機制。RedisBroadcaster
會將事件中的容許公開訪問的數據經過給定的頻道發佈出去。
對於發佈出去的信息,咱們須要一個服務來對接,讓其能對 redis 的發佈可以進行訂閱,而且能把信息以 WebSocket 協議轉發出去,這裏咱們能夠借用 Node.js 和 socket.io 來很是方便的構建這個服務:
let app = require('http').createServer((req, res) => { res.writeHead(200); res.end(''); }); let io = require('socket.io')(app); //redis能夠配置 let Redis = require('ioredis'); var evn = 'test'; if(evn == 'local'){ var host = '127.0.0.1'; var port = '6379'; var password = null; } else if(evn == 'test'){ var host = '186.5.562.2'; var port = '6379'; var password = '151554145'; } let redis = new Redis({ 'host':host, 'port':port, 'password':password }); //存儲用戶 var userList = []; app.listen(6001, () => console.log('Server is running!')); io.on('connection', (socket) => { socket.on('login', (user) => { if (userList[user.channel] === undefined) userList[user.channel] = []; if(user.openid.indexOf("admin") == -1){ userList[user.channel].push(user.openid); io.emit(user.channel + ':UserChange', userList[user.channel]); let userCount = countUser(); io.emit('user.count', userCount); } console.log('[L] openid:' + user.openid + ' Login!'); }); socket.on('disconnect', (res) => { let user = cookieToObject(socket.request.headers.cookie.split('; ')); let openid = user['openid'] + ""; if(openid.indexOf("admin") == -1){ let index = userList[user['channel']].indexOf(user['openid'] + ""); userList[user['channel']].splice(index, 1); io.emit(user.channel + ':UserChange', userList[user.channel]); let userCount = countUser(); io.emit('user.count', userCount); } console.log('[L] openid:' + user['openid'] + ' Logout!'); }); socket.on('user.count', () => { let userCount = countUser(); io.emit('user.count', userCount); }); }); redis.psubscribe('*', (err, count) => { }); redis.on('pmessage', (subscrbed, channel, message) => { message = JSON.parse(message); console.log('[M]'.channel + ' Message :' + message.event, message.data); io.emit(channel + ':' + message.event, message.data); }); function countUser() { let userCount = []; for (var i in userList) { userCount[i] = userList[i].length; } return userCount; } function cookieToObject(data) { let new_Object = []; for (let i in data) { data[i] = data[i].split('='); new_Object[data[i][0]] = data[i][1]; } return new_Object; }
這裏咱們使用 Node.js 引入 socket.io 服務端並監聽 6001 端口,借用 redis 的 psubscribe 指令使用通配符來快速的批量訂閱,接着在消息觸發時將消息經過 WebSocket 轉發出去。
在 web 前端,咱們須要引入 Socket.io 客戶端開啓與服務端 6001 端口的通信,並訂閱頻道事件:
$.cookie('channel', channel_id, {path: "/"}); $.cookie('openid', user, {path: "/"}); var socket = io(':6001'); socket.on('connect', function () { socket.emit('login', {channel: channel_id, openid: user}); }); socket.on(channel_id + ':removeMessage', function (res) { $(".message-list .message-item[data-id='"+res.id + "']").remove(); });
最後在使用node在後臺啓動server.js
至此整個通信閉環結束,開發流程看起來就是這樣的: