前言

在前端开发中,我们有时会遇到一些需要websocket的情况,而一个系统里如果有多个地方需要用到websocket,我们就会去写很多重复的处理代码,所以这边自己封装了websocket的类。

一些需要websocket的场景,比如:

1.异步等待服务状态,可以实时获取后端通过k8s创建服务的流程信息

2.web终端,可以使用websocket与后端实时同步输入与输出

3.大屏图表,一些比较重要的数据是需要查看实时的

封装思路

首先构造函数中可以设置websocket的连接地址与socket的实例等信息,传参只需要一个连接地址,因为前端并没有方法的重载,所以这些重连和心跳不是直接在构造函数添加入参,而是直接通过独立的方法手动设置

封装类代码(重连估计还有点小问题😄)

export class WebsocketManager {
    constructor(url) {
        // 连接地址
        this.url = url;
        this.socket = null;
        // 心跳定时器
        this.heartTimer = null;
        // 重连次数
        this.reconnectCount = 0;

        //普通定时器
        this.timer = null;
        //连接开始时间
        this.openTime = null;
        //连接断开时间
        this.closeTime = null;
    }

    init() {
        // 初始化websocket
        this.socket = new WebSocket(this.url);
        this.handleSocketOpen();
        this.handleSocketMessage();
        this.handleSocketClose();
        this.handleSocketError();
    }

    /**
     * 建立心跳机制
     * @param {*} heartbeat 是否开启心跳
     * @param {*} heartbeatInterval 心跳间隔
     * @param {*} heartbeatInfo 心跳发送信息
     */
    setHeartbeat(heartbeat = false, heartbeatInterval = 1000, heartbeatInfo = '') {
        if (heartbeat)
            // 开启心跳
            this.heartTimer = setInterval(() => {
                this.socket.send(heartbeatInfo);
            }, heartbeatInterval);
        else {
            // 关闭心跳
            clearInterval(this.heartTimer);
            this.heartTimer = null;
        }
    }

    /**
     * 建立重连机制
     * @param {*} reconnect 是否重连
     * @param {*} maxReconnectCount 最大重连次数
     * @param {*} reconnectDelay 尝试重连延时
     */
    setReconnect(reconnect = false, maxReconnectCount = 9999, reconnectDelay = 5000) {
        this.reconnect = reconnect;
        this.maxReconnectCount = maxReconnectCount;
        this.reconnectDelay = reconnectDelay;
    }

    // 处理连接打开事件
    handleSocketOpen() {
        this.socket.onopen = (event) => {
            console.log('socket open:', event);
            this.openTime = new Date();
            console.log('socket open time:', this.openTime);
        };
    }

    // 处理接收到消息事件
    handleSocketMessage() {
        this.socket.onmessage = (event) => {
            console.log('socket open:', event);
        };
    }

    //处理连接关闭事件
    handleSocketClose() {
        this.socket.onclose = (event) => {
            console.log('socket close:', event);
            this.closeTime = new Date();
            console.log('socket close time:', this.closeTime);
            console.log('socket continue period:', this.closeTime - this.openTime);

            // 清除心跳计时器
            this.setHeartbeat(false);

            // 是否支持重连
            if (this.reconnect) {
                //是否超过最大重连次数
                if (this.reconnectCount <= this.maxReconnectCount) {
                    this.reconnectCount++;
                    this.timer = setTimeout(() => {
                        console.log('重连');
                        this.init();
                    }, this.reconnectDelay);
                } else {
                    // 重置最大重连次数
                    this.reconnectCount = 0;
                    console.error('超过最大重连次数');
                }
            }
        };
    }

    // 处理 WebSocket 错误事件
    handleSocketError() {
        this.socket.onerror = (event) => {
            console.error('socket error-' + new Date(), event);
        };
    }

    closeWebSocket() {
        this.socket.close();
        //清除心跳计时器
        this.setHeartbeat(false);
        // 重置重连次数
        this.reconnectCount = 0;
    }
}

使用方法

实现一个类去继承这个websocket封装类,有需要心跳或者重连机制的话可以重写init类,也可以重写接受消息的方法,如下部分vue代码示例

class DevServiceSocket extends WebsocketManager {
    constructor(url) {
        super(url);
    }

    init() {
        super.init();
        this.setHeartbeat(true, 60000);
    }
    
    handleSocketMessage() {
        this.socket.onmessage = (event) => {
            const record = JSON.parse(event.data);
            handleDevSocketMessage(record);
        };
    }
}

let devServiceSocket  = null

function initDevSocket() {
	const url = 'xxx'
	devServiceSocket = new DevServiceSocket(url)
	devServiceSocket.init()
}

onMounted(() => {
	initDevSocket()
})

onBeforeUnmount(() => {
  devServiceSocket.closeWebSocket()
})