前端 js 如何建立和管理多个websocket连接?

js 如何建立和管理多个websocket连接

发送A请求, 建立连接A, 处理B请求
A连接还在处理请求中
发送B请求, 建立连接B, 处理B请求
A连接处理完成, 释放, 空闲状态
发送C请求, 使用连接A, 处理C请求

发送请求时, 有空闲的连接, 则使用空闲的, 若无则创建新的连接.

这种websocket该怎么写? websocke返回的数据该存放到哪里?

我目前现在的方案是只建立一个连接的websocket
连接时判断存在连接, 则直接使用该连接. 将接收到的数据存放发vuex的属性中, 然后在页面中watch 监听该属性, 进行后续操作.

如果建立多个连接, 将多个连接的返回结果也都存vuex中同一个变量中, 可行吗?

阅读 6.8k
5 个回答

应当考虑 webSocket 连接的复用而不是建立多个 webSocket
因为WebSocket天生只有会话属性,而没有请求属性,这样设计是因为后端可以主动推送,如果需要前端先发请求的话后端就不具备主动性了。
但是作为开发者,是可以在它的基础上自己实现类似 http 的请求-回应机制的,方法很简单——给每一个请求加上一个 ID ,后端回应的时候也带上这个 ID 即可:

class EventBus{
  #handlerMap = new Map();
  #idRecord = 0;
  listen(handler){
    const id = (this.#idRecord++) + '';
    this.#handlerMap.set(id, handler);
    return id
  }
  trigger(id, message){
    this.#handlerMap.get(id)(message);
    this.#handlerMap.delete(id);
  }
}

class SocketChannel {
  constructor(url){
    this.#channel = new WebSocket(url);
    this.#eventBus = new EventBus();

    this.#channel.addEventListener('message', (response) => {
      const { requestId, message } = JSON.parse(response);
      this.#eventBus.trigger(message);
    })
  }

  addtask(info, callback){
    const requestId = this.#eventBus.listen(callback);
    // 注意,这里将 requestId 传给了后端,以作为任务的 ID
    // 这意味着后端处理好的消息里也必须带上这个 ID
    this.#channel.send(JSON.stringify({
      requestId,
      info
    }));
  }
}
👆相当于实现了一个类似 http 这样的协议,但是“协议”嘛,自然是需要双方都遵守才行。
这个“协议”就需要你和后端约定使用requestId来定位处理任务,如果后端搞不定这一点的话那么你们应该降级使用 http 长连接。

为什么会有这样的需求?建立连接的开销也是不小的呀。

对于消息的处理,本就不一定是同步的呀,你只需要 send ,后端是可以以非同步的方式去同时处理这些任务,在处理完成后,服务端 send ,你在 message 里面处理就好了呀。

对于找个需要,简单写了一个,未经测试。

// 记录开启了多少个 ws 连接
let count = 0;
// 用来存放 ws 的连接
const pool = [];
// 用来存放因 ws 连接过多的任务队列
const queue = [];

setInterval(function () {
  if (queue.length) {
    let task
    while (task = queue.pop()) {
      doAction(...task)
    }
  }
}, 1000)

function doAction (data, callback) {
  // 拿出一个连接
  var ws = pool.pop()
  if (!ws || [2, 3].includes(ws.readyState)) {
    if (count >= 5) {
      // 如果已经建立的连接太多了,还是限制一下吧。
      queue.push([...arguments])
      return;
    }
    ws = new WebSocket(/**TODO*/);
    ++count;
  }

  ws.addEventListener('open', (event) => {
    ws.send(data)
  });

  ws.addEventListener('message', function () {
    // TODO
    callback(...arguments)

    // TODO
    pool.push(ws)
  })

  ws.on('close', () => --count)
}

doAction('a1你好~', function () {
  console.log('a1');
})

doAction('a2你好~', function () {
  console.log('a2');
})

doAction('a3你好~', function () {
  console.log('a3');
})

后端是单线程,那还用什么websocket啊,直接用http请求就好了啊。

这个请求也相当于连接池。

找个数组变量保存 ws对象。
请求的时候,如果数组是空,就创建一个ws放数组中。
如果数组内有ws对象,那就直接pop一个出来,执行send,监听message事件,在message中,再把这个ws对象归还到连接池。

建议和后端沟通, 只启动一个ws连接, 后端去负责并发

前端不用做处理啊,本来就是异步分开的。你的描述看起来像是后端的锅,得让后端来处理。

听你的意思是池化连接?你可以搜一搜有没有现成的websocket前端池化连接lib,这种往往有现成的解决方案。就像后端的Druid db连接池一样

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏