使用stompjs实现Websocket即时通讯订阅、发布

日常开发中很多时候会使用到即时通讯 ,实现消息实时更新的方式

1、使用轮询进行查询(如果状态在某一特定情况下长时间没有改变,客户端频繁的调用接口会造成资源浪费)

2、使用websocket

以下实例使用的是stompjs和sockjs-client,目的是达到消息的订阅、发布

npm  install --save stompjs

npm  install --save sockjs-client

废话不多说,直接上代码

1、以下可封装独立文件 stompManager.js

import Stomp from 'stompjs'
import SockJS from 'sockjs-client'

const ip = '192.168.4.46' // 可根据外部变量进行配置
const stompManager = {
    url: 'http://'+ ip +':8081/web', // 连接地址(前后端自行定义)
    header: null, // 连接header
    checkInterval: null,//断线重连时 检测连接是否建立成功
    websocket: null,
    stompClient: null,
    listenerList: [],//监听器列表,断线重连时 用于重新注册监听(主要是在其他页面后添加的订阅)
    // isReconnect: true, // 是否需要重连(可根据配置自行定义是否重连)
    init(header) {
        console.log('stompManager---init-----', this)
        // header中的内容根据需要进行传递,例:{userId: store.state.user.userId,type: 'WEB'}
        this.header = header 
        this.listenerList = []
        // this.isReconnect = true
        this.connect()
    },
    connect(){
        if(this.stompClient != null && this.websocket.readyState === SockJS.OPEN){ // 已连接
            this.stompClient.disconnect(()=>{
                this.connect()
            })
            return ;
        }else if(this.stompClient != null && this.websocket.readyState === SockJS.CONNECTING){ // 连接中
            // console.log("连接正在建立")
            return;
        }
        const _this = this
        this.websocket = new SockJS(this.url);

        // 获取STOMP子协议的客户端对象
        const stompClient = Stomp.over(this.websocket);
        stompClient.debug = null //关闭控制台打印
        stompClient.heartbeat.outgoing = 20000;
        stompClient.heartbeat.incoming = 0;//客户端不从服务端接收心跳包
        // 向服务器发起websocket连接
        stompClient.connect(
            this.header,  //此处注意更换自己的用户名,最好以参数形式带入
            frame => {
                console.log('链接成功!')
                _this.listenerList.forEach((item, index)=>{
                    _this.listenerList[index].subscription = _this.stompClient.subscribe(item.topic,item.callback)
                })
                //unsubscribe()可以用来取消客户端对这个目的地destination的订阅
                // stompClient.subscribe("/user/queue/message", msg => {
                //   // this.getData(msg);
                //   console.log(msg)
                // });

                // 订阅红点消息目的地 连接建立成功后直接进行订阅
                _this.stompClient.subscribe('/user/queue/message', function(response) {
                    const data = JSON.parse(response.body)
                    console.log(data) // 根据情况自行定义
                })
            },
            err => { // 第一次连接失败和连接后断开连接都会调用这个函数 此处调用重连,主动调用disconnect不会走这里
                // console.log('stompClient-----connect---error----', err)
                // if (_this.isReconnect) { // 需要重连
                    setTimeout(() => {
                        _this.connect()
                    }, 2000)
                // }
            }
        );
        this.stompClient = stompClient
    },
    disconnect() {
        // this.isReconnect = false
        if (this.stompClient && this.stompClient.connected) { // 当前已连接
            this.stompClient.disconnect(()=>{
                console.log('主动断开连接,不重连---------')
            })
        }
    },
    send(topic, params, callback, responseTopic) { // 外部调用,发送消息
        if (this.stompClient && this.stompClient.connected) { // 当前已连接
            if (responseTopic) { // 此消息发送后,有响应topic对应回复消息
                var i = 1
                var self = this
                this.subscribe(responseTopic, function(response) {
                    console.log(responseTopic+'-------', response)
                    var data = JSON.parse(response.body)
                    if (data.status == '200') { // 请求成功
                        self.stompClient.unsubscribe(responseTopic)
                        if (callback) callback({success:true, data:data})
                    }else { // 请求失败
                        console.log('重复发送次数--------', i)
                        if (i >= 5) { // 超过5次则不再请求
                            self.stompClient.unsubscribe(responseTopic)
                            if (callback) callback({success:false, msg:data.msg})
                        }else {
                            i++
                            setTimeout(() => {
                                self.stompClient.send(topic,{},JSON.stringify(params));
                            }, 2000);   
                        }
                    }
                })
            }
            this.stompClient.send(topic,{},JSON.stringify(params));
        }else {
            if (callback) callback({success:false, msg:'未连接'})
        }
    },
    unsubscribe(topic){ // 外部调用,解除订阅
        for(let i=0;i<this.listenerList.length;i++){
            if(this.listenerList[i].topic == topic){
                var subscription = this.listenerList[i].subscription
                if (subscription) {
                    subscription.unsubscribe()
                }
                this.listenerList.splice(i,1)
                console.log("解除订阅:"+ topic +" size:"+this.listenerList.length)
                break;
            }
        }
    },
    subscribe(topic, callback) { // 外部调用,订阅
        if (this.stompClient && this.stompClient.connected) {
            if (this.listenerList.some(item => item.topic == topic)) { // 之前有订阅过,需要解除订阅
                this.unsubscribe(topic)
            }
            var subscription = this.stompClient.subscribe(topic, callback)
            this.listenerList.push({
                topic: topic,
                callback: callback,
                subscription: subscription
            })
        }else {
            var flag = false
            for(let i=0; i<this.listenerList.length; i++){
                if(this.listenerList[i].topic == topic){
                    flag = true
                    this.listenerList[i].callback = callback
                    console.log("订阅:"+ topic +" size:"+this.listenerList.length)
                    break;
                }
            }
            if (!flag) { // 之前没有监听此topic
                this.listenerList.push({
                    topic: topic,
                    callback: callback
                })
            }
        }
    }
}

export default stompManager

2、main.js 文件引入

import Vue from 'vue'
import socket from './libs/stompManager' // 根据文件路径自行修改

Vue.prototype.$socket = socket // 全局配置

3、连接和断开调用(方案可采用,登录操作和刷新页面时进行连接,退出登录进行断开)

import stompManager from '@/libs/stompManager' // 注意文件地址

stompManager.disconnect() // 断开websocket连接

stompManager.init(header) // 初始化连接 header type:Object

4、其他文件调用订阅、取消订阅

// 订阅
this.$socket.subscribe('/topic/publish/', (response)=>{
    var data = JSON.parse(response.body)
})

// 取消订阅
this.$socket.unsubscribe('/topic/publish/')

这是我本人在工作学习中做的一些总结,同时也分享出来给需要的小伙伴哈 ~ 供参考学习,有什么建议也欢迎评论留言,转载请注明出处哈,感谢支持!

猜你喜欢

转载自blog.csdn.net/jiangzhihao0515/article/details/132834763