百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

websocket长连接和公共状态管理方案

toyiye 2024-06-21 12:42 10 浏览 0 评论


为什么将websocket和公共状态管理扯到一起

我们都知道在vue和react这种单页面组件化项目中,建立socket连接会遇到:重复连接切换页面连接中断 ,状态丢失等问题,而且如果想要在任何页面接受到来自socket传递的信息,所以在建立socket连接时候就要考虑是否要把连接实例化放在公共state里边统一管理,这样可以方便在任何组件中调用socket方法。这里会介绍socket与Vuex和redux进行连接实时接受信息改变数据的方案。

此方案根本解决问题:

① 根本上解决单页面组件重复连接,切换页面组件连接中断,状态丢失等问题。 ② 状态统一管理,统一调度中心。任意页面共享数据源,任意页面实现推送数据。 ③ socket连接层面和组件层面的耦合程度降到最低。

websocket与公共状态管理逻辑图

总结

本方案的大体思路就是如上图所示,现在页面初始化的时候根据需要向vuex或者redux发起dispatch触发初始化的方法,初始化的时候触发websocket,js构造函数或者类的实例,并且要把改变公共状态方法的commit作为参数传递给socket实例 , 而真正建立起socket连接的方法实在webosocket实例中进行的,websocket实例会暴露出两个方法,一个subscribe用来监听服务端传递的信息来改变管理状态,当然这里的触发是根据调用commit函数来触发的,另一个是又任意组件调用的emit方法 ,来把信息传递给服务端,从而实现了双向通信,并把通信回执内容放在公共状态管理,避免切换组件信息丢失,重新连接,丢失连接等情况发生。下面会拿vuex例子具体讲一下流程。

成功案例(websocket与vue及vuex为例子)

方案结构及其初始化流程

目录文件

以上就是文件的格式(这里简化了), websocket.js就是socket调度中心(此方案的核心),里边集成了订阅器发布器失败调度心跳机制的等等 , vuex下边的socket.js就是一个vuex模块, 在dva中可以理解成一个model, socket.vue就是要用到socket连接的组件,废话不说,下面一一解释.

在页面组件中初始化

首先我们来看socket初始化

 if (!socket.ws) {
            //在socket.vue文件中初始化socket连接
     this.$store.dispatch('socketInit')
 }
复制代码

这是只是单独触发了一个dispatch , 在调用了一个socketInit方法,然后我们来看vuex中socket.js中的socketInit方法。

import Socket from '../websocket' //socket 方法类
import socketAction from '../../config/socket' //这个是对服务端的数据处理的中间件函数,这里可以忽略

export default {
    state: {
        ws: null, // websorket实例
    
    },
    mutations: {
        subscribe_socket (state,{data}){
            //这里的data为socket连接后端返回来的数据
        },
        contentSocket (state, { commit }) {
            state.ws = new Socket(commit, socketAction)
        }
    },
    actions: {
        // 创建实例
        socketInit ({commit, state}) {
            commit('contentSocket', { commit }) //把commit作为参数
        }
    }
}
复制代码

在vuex的异步函数actions调用了初始化的方法,然后把触发contentSocket 发法来创建实例,并绑定在state上的ws上,这里一定要把commit 来作为参数,一边socket实例能触发方法改变state,我们知道了socket实例如何绑定和commit传递的了 ,下面我们看看websocket.js 整个核心调度是怎么运作的。

socket核心调度

function socket (commit, actions) {
    if (isType(commit) !== 'Function') {
        throw new Error('commit must be a function')
    }  
    this.commit = commit //触发vuex中mutations的commit
    this.actions = actions || null
    this.timer = null
    this.errorResetNumber = 0      // 错误重连间隔
    this.closeWs = false
    this.errorFrom = 0             // socket断开来源
    this.errorResetTimer = null    // 错误重连轮询
    this.errorDispatchOpen = true  // 开启错误调度
    this.heartSocketOpen = false   // 心跳
    isSocketContent()
    this.$soctket_init() //
}

复制代码

我们看到了websocket函数是一个构造函数用来做初始化操作, isSocketContent()是用来获取token等操作大家不必在意, 这里触发了一个socketinit()方法,接下来我们看一下socket_init()方法,接下来我们看一下socketinit()方法,接下来我们看一下soctket_init()方法

socket.prototype.$socket_init = function (callback) {
    const _this = this
    if (_this.closeWs) {
        throw new Error('socket is closed ,$socker_init is fail ,  all methods is invalid')
    }
    const token = window.localStorage.getItem('token') || window.sessionStorage.getItem('token') || null

    if (!token) {
        throw new Error('token  is underfined')
    } 
    const handerErrorMachine = () => { 
        if (_this.errorResetNumber === 4) {
            _this.errorResetNumber = 0
            _this.errorResetTimer = null
            _this.errorFrom = 0
            _this.errorDispatchOpen = false
            _this.ws = null
            console.log('socket连接失败')
            return
        }
        _this.errorResetTimer = setTimeout(() => {
            /* 失败重新连接 */
            _this.$soctket_init()
            _this.errorResetNumber++
        }, _this.errorResetNumber * 2000)
    } 
    
    const errorDispatch = (eventment) => { //错误调度
        let event = eventment
        return function () {
            if (_this.errorFrom === 0 && _this.errorDispatchOpen) {
                _this.errorFrom = event
            }
            event === 1 ? console.log('web socket has failed  from closeState ') : console.log('web socket has failed  from errorState ')
            if (_this.errorFrom === event && !_this.closeWs) {
                _this.errorResetTimer && clearTimeout(_this.errorResetTimer)
                handerErrorMachine()
            }   
        }
    }
    if (this.timer) clearTimeout(this.timer)

    _this.ws = new WebSocket(socketUrl + '?token=' + token) //这里才进行了真正的socket连接

    _this.ws.onopen = function () {
        callback && callback()
        _this.errorResetNumber = 0
        _this.errorResetTimer = null
        _this.errorFrom = 0
        _this.errorDispatchOpen = true
        /* 接受消息,改变状态 */
        _this.$soctket_subscribe()
        _this.$soctket_heartSoctket()
        console.log('web socket has connected ')
    }

    _this.ws.onclose = errorDispatch(1)
    _this.ws.onerror = errorDispatch(2)
}
复制代码

这里才是真正的socket连接 和一些错误处理方式 , 这里把socket连接和构造函数中的ws绑定在一起,以及一个连接失败的调度机制 , 里边有一个之前一直提到的方法,socketsubscribe()没错就是它,监听后端传来信息的方法,并且触发vuex,commit方法,改变state,通知view视图更新,值得提出的一点是.socket_subscribe() 没错就是它,监听后端传来信息的方法,并且触发vuex ,commit方法,改变state,通知view视图更新,值得提出的一点是.sockets?ubscribe()没错就是它,监听后端传来信息的方法,并且触发vuex,commit方法,改变state,通知view视图更新,值得提出的一点是.socket_heartSoctket() 是一个心脏搏动机制,我们知道如果socket连接长时间没有通话会自动断开连接,所以这里有一个心脏搏动机制。接下来我们看一下,socket_subscribe 方法。

subscribe订阅接受信息,改变状态

/**
* 接受广播 -> 督促view更新
*/

socket.prototype.$socket_subscribe = function () {
    const _this = this
    _this.ws.onmessage = function (res) {
        if (_this.actions) {
            if (isType(_this.actions) !== 'Function') {
                throw new Error('actions')
            } else {
               
                _this.commit(..._this.actions(res.data))
            }
        } else {
            _this.commit(res.data)
            
        }    
        _this.$soctket_heartSoctket()
    }
}
复制代码

我们才看到原来之前vuex传进来的 commit 在这里发挥了作用,也就是触发mutations 来改变state里边 的数据 ,来重新渲染试图 ,接下来我们看一下emit触发器。

emit 任意组件传递信息

 /**
* 触发器->发布信息
* @param callback 状态处理
* @param value 数据处理
*/
socket.prototype.$socket_emit = function (value, callback) {
    const _this = this
    const poll = function () {
        return _this.ws.readyState
    }
    if (callback && isType(callback) !== 'Function') {
        throw new Error('$socket_emit arugment[1] must be a function')
    }
    if (!_this.ws) {
        throw new Error('$socket dispatch is fail please use $socket_open method')
    }
    if (_this.ws.readyState === 1) { // 连接成功状态
        _this.ws.send(value)
        _this.$soctket_heartSoctket()
        callback && callback()
    }
    else if (_this.ws.readyState === 0) { // 连接中状态 ,轮询查询连接
        eventPoll(poll, 1, 500, () => {
            _this.ws.send(value)                                                             
            _this.$soctket_heartSoctket()
            callback && callback()
        })
    }
    else { // 失败重新连接
        _this.$soctket_init(() => {
            _this.$soctket_emit(value, callback)
        })
    }
}
复制代码

这个就是之前提到的emit 触发器 用来在vue中调用, 来向服务端发起数据通信,就实现了双向的数据通信, 里边有一个轮询器 来轮询eventPoll ,websocket 的状态是否是已经连通的状态 ,那么在Vue文件中是怎么调用emit的呢 ,很简单就是调用vuex中之前绑定的state里边的wx。

任意组件中

 const { ws } = this.$store.state.socket
   ws.$soctket_emit(JSON.stringify({
                data: 'hello , world'
    }), () => {
        console.log('发送成功')
    })
复制代码

就是这么简单触发的。以上整个机制都已经讲解了一边,那么还有心跳机制,给大家介绍一下。

heart心跳机制

/**
* 心脏搏动机制->防止断开连接
*/
socket.prototype.$soctket_heartSoctket = function () {  
    if (this.timer) clearTimeout(this.timer)
    console.log(this.timer)
    this.timer = setTimeout(() => {
        if (this.ws.readyState === 1 || this.ws.readyState === 0) {
            this.ws.send('heart , socket')
            this.$soctket_heartSoctket()
        } else {
            this.$soctket_init()
        }
    }, 59000)
复制代码

就是不断向服务端发起消息,来防止断开连接。 还有两个方法来控制ws的连接和关闭。

/**
* 开启,关闭 socket
*/
/**
* 关闭socket连接
*/
socket.prototype.$soctket_close = function () {
    if (this.timer) clearTimeout(this.timer)
    if (this.errorResetTimer)clearTimeout(this.errorResetTimer)
    this.closeWs = true
    this.ws.close()
}
/**
* 重启socket连接
*/
socket.prototype.$soctket_open = function () {
    if (!this.closeWs) {
        throw new Error('socket is connected')
    }
    this.timer = null
    this.errorResetNumber = 0
    this.closeWs = false
    this.errorFrom = 0
    this.errorResetTimer = null
    this.errorDispatchOpen = true
    this.heartSocketOpen = false
    this.closeWs = false
    this.$soctket_init()
}

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码