laraval+node.js實現websocket

本文是使用laraval的event與node.js做爲websocket服務器,與頁面實現長鏈接;php

基本知識前端

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

配置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=rediscookie

下面是一個事件類的例子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 會將事件中的容許公開訪問的數據經過給定的頻道發佈出去。

Node.js 和 Socket.io

對於發佈出去的信息,咱們須要一個服務來對接,讓其能對 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 轉發出去。

Socket.io 客戶端

在 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

至此整個通信閉環結束,開發流程看起來就是這樣的:

  • 在 Laravel 中構建一個支持廣播通知的事件
  • 設置須要進行廣播的頻道及事件名稱
  • 將廣播設置爲使用 redis 驅動
  • 提供一個持續的服務用於訂閱 redis 的發佈,及將發佈內容經過 WebSocket 協議推送到客戶端
  • 客戶端打開服務端 WebSocket 隧道,並對事件進行訂閱,根據指定事件的推送進行響X
相關文章
相關標籤/搜索