【问题标题】:Websockets clients crash on high message rateWebsockets 客户端因高消息率而崩溃
【发布时间】:2018-03-05 15:55:28
【问题描述】:

我的 javascript websocket 客户端在高速接收消息时崩溃。我已经尝试了 3 个浏览器,它们都崩溃了。首先是 Chrome,其次是 IE,Edge 可以保持更长时间。

消息速率约为 30 条消息在不到一秒的时间内。大约 10-20 个字符长(数据部分)。

onmessage 调用是异步的吗?它们可以并行运行(下一个 onmessage 在上一个完成之前已经开始)还是将请求放在某种队列中?

我试图“阻止” onMessage 无济于事。

var blockWS = false;

function pageLoad() {
  if ('WebSocket' in window) {
    var ws = new WebSocket(((window.location.protocol === 'https:') ? 'wss://' : 'ws://') + window.location.host + '/ws');
    ws.onmessage = function(message) {
      while (blockWS) {
        // wait
      }
      blockWS = true;
      var el = document.getElementById('printer');
      el.innerHTML += message.data;
      el.scrollTop = el.scrollHeight;
      blockWS = false;
    };
  } else {
    // The browser doesn't support WebSocket
    document.getElementById('body').innerHTML = '<h1>Your browser does not support WebSocket.</h1>';
  }
}
<html>

<body onload="pageLoad()">
  <div id="body">
    <div id="printer"></div>
  </div>
</body>

</html>

【问题讨论】:

  • 如果不对这段代码进行性能测试,我会假设您的浏览器崩溃了,因为您每次收到新消息时都在尝试读取和修改完全相同的 DOM 元素。在 JavaScript 中读取/写入 DOM 非常昂贵且耗时。
  • @th3n3wguy 你可能*是对的。当我降低消息率时,一切正常。 (* 因为我没有配置文件)
  • 顺便说一句,blockWS 的东西没有做任何事情。我认为您正在尝试使用它来防止两个 onmessage 处理程序试图同时运行时出现问题。但这无论如何都不是问题,因为处理程序没有做任何异步操作。
  • 您的意思是总共只有 30 条消息并且需要一秒钟,还是以每秒 30 条消息的速度一直持续下去?
  • 服务器以突发方式发送(突发间隔为 30-60 秒)。但是在发送时,它以 30/秒的速度发送。所以如果它不是异步的,如果ws 试图在上一次调用完成之前调用处理程序会发生什么?

标签: javascript websocket


【解决方案1】:

做你想做的事情的简单(不确定这里的性能)方法是在循环外使用setInterval() 方法,并在消息数组从websockets 进来时简单地存储它们。下面是一些你可以做的测试代码:

编辑:我添加了代码的更新部分,以便只有在需要显示新消息时才会插入到 DOM。

// Your code that is crashing the browser:
/*var blockWS = false;

function pageLoad() {
  if ('WebSocket' in window) {
    var ws = new WebSocket(((window.location.protocol === 'https:') ? 'wss://' : 'ws://') + window.location.host + '/ws');
    ws.onmessage = function(message) {
      while (blockWS) {
        // wait
      }
      blockWS = true;
      var el = document.getElementById('printer');
      el.innerHTML += message.data;
      el.scrollTop = el.scrollHeight;
      blockWS = false;
    };
  } else {
    // The browser doesn't support WebSocket
    document.getElementById('body').innerHTML = '<h1>Your browser does not support WebSocket.</h1>';
  }
}*/

// Using the setInterval method (I used 1s, but you can change that to your preferences):
function pageLoad() {
  const MESSAGE_INSERT_TIME_MS = 1000;
  let messages = [];
  let intervalId = null;
  
  if ('WebSocket' in window) {
    const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
    const ws = new WebSocket(`${protocol}${window.location.host}/ws`);
    
    ws.onmessage = (message) => {
      messages.push(message);
    };
    
    // At any point inside the "pageLoad()" function, you can use "intervalId.clearInterval()" to stop doing this function.
    intervalId = setInterval(() => {
      const el = document.getElementById('printer');
      
      if (!el.innerHTML === messages.join('')) {
        el.innerHTML = messages;
        el.scrollTop = el.scrollHeight;
      }
    }, MESSAGE_INSERT_TIME_MS);
  }
}

【讨论】:

  • 但是你每秒都在更新 DOM,即使没有更多消息。这不是最优的。您可以在setInterval 处理程序中添加一个条件来检查是否需要更新。虽然我更喜欢使用lodash.com/docs/4.17.5#throttle(或者如果你不使用lodash,你自己重写_.throttle)。
  • 如果运行时间过长,这会消耗大量内存。更新 DOM 后无需将消息保留在数组中...
  • @d3L => 是的。正如我最初所说,我不确定它在长期内的表现如何。我没有清除消息的唯一原因是,当浏览器从 DOM 读取以获取显示的当前消息列表时,可以将消息添加到消息数组中。
  • @th3n3wguy 设置innerHTML 是根据stackoverflow.com/questions/42986295/is-innerhtml-asynchronous 同步的,所以这永远不会发生。
  • @th3n3wguy 也许,但消息仍然不会丢失。 JavaScript 是单线程的stackoverflow.com/questions/10517835/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 1970-01-01
  • 2019-03-30
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
相关资源
最近更新 更多