websocket 的严重性能问题 - 每条消息都会触发 Angular 更改检测

Posted

技术标签:

【中文标题】websocket 的严重性能问题 - 每条消息都会触发 Angular 更改检测【英文标题】:Terrible performance issues with websocket - every message triggering Angular change detection 【发布时间】:2017-06-05 14:24:06 【问题描述】:

我有一个 websocket,我目前正在以大约每秒 45 条的速度流式传输消息。在这个级别上,它完全杀死了铬。浏览器窗口中的所有功能都已锁定。

我已经缩减了我的实现,试图找出这个问题的根本原因,我怀疑这可能是我处理这些消息的方式存在问题 - 或者在 Angular 中更新/更改检测。

但现在我只剩下我能得到的最简单的 websocket 实现了:

this.socket = new WebSocket('ws://localhost:5000/ws');

let i = 0;
this.socket.onmessage = (e: MessageEvent) => 
  i++;
  if (i % 100 === 0) 
    console.log('recieved' + i);
  
;

这是一个 Angular Injectable,但它不与任何东西交互。

它每 100 条消息写入控制台。这仍然会以 100% 的 CPU 使用率杀死浏览器,在我停止消息流之前,它甚至大部分时间都不会输出到控制台,然后一切都赶上了,它会喷出几行“received x00”消息。

消息本身是 JSON,如下所示:


  "Topic":"2a736d15-a2fe-43b2-8e8b-ee888f15a53a","Type":1,
  "Message": 
     "Value":2,
     "Timestamp":"2017-06-05T14:46:21.615062+01:00"
   

它们通常约为 126 个字符。

我假设 websockets 每秒可以处理数万条消息,但我在 google 上找不到任何合理的指标,这听起来像不合理的性能吗?

我还检查了 CPU 分析器。当一切都被锁定时,它只是一堵巨大的电话墙,所以我把它降低为每秒一条消息:

深入研究其中一个尖峰(每条消息都会发生!):

我在其中一个位置设置了一个断点,每当 websocket 被触发时,Angular 似乎都在做某事,这与区域有关(我对此的经验为零!):

堆栈中的第一件事是 Websocket 本身的 ZoneTask.invoke,但随后 NgZone 被触发,最终调用了一个非常昂贵的更改/更新链:

这一切怎么会发生?订阅 websocket 的 7 行代码完全独立于 Angular 的变更检测,对吧?它们不会更改绑定到任何组件的任何值。他们唯一的链接是他们坐在@Injectable() 内的方法中

【问题讨论】:

看这篇文章并使用 WebWorkers/ServiceWorkers 进行研究:***.com/questions/17998011/… 【参考方案1】:

Sods 法律,只要我发布一个问题,我就会找到答案。通常我会删除它,但我找不到太多关于该主题的内容,因此它可能对某人有用。

这是 Zones 的问题。这是useful blog post。一般来说,Angular 2 使用区域来驱动它的变更检测:

Angular 2 使用 zone.js 的原因是要知道我们的处理程序何时完成,然后 NgZone 服务(NgZone 是一个包装区域服务的类)调用 ApplicationRef.tick() 方法。 tick() 方法从上到下扫描树组件,并在每个组件中计算模板中存在的表达式。如果表达式的结果不等于前一个结果,(从前一个刻度)Angular 将更新连接到这个表达式的 DOM 属性。

一个简单的单独获得消息性能的简单解决方案是在区域外运行,从而使用方便的方法runOutsideAngular 进行 Angular 的更改检测:

this.zone.runOutsideAngular(() => 
  let i = 0;
  this.socket.onmessage = (e: MessageEvent) => 
    i++;
    if (i % 100 === 0) 
      console.log('recieved' + i);
    
  ;
);

【讨论】:

以上是关于websocket 的严重性能问题 - 每条消息都会触发 Angular 更改检测的主要内容,如果未能解决你的问题,请参考以下文章

“在 [location] 找到的 adb 二进制文件已过时并且存在严重的性能问题”但模拟器运行!!显示消息后

大型聊天应用程序 Mysql db 消息存储每条消息 1 行或将整个线程附加到 1 个长文本字段

高可靠高性能的消息队列怎么实现?

考虑到每条消息应用标签,2人如何使用相同的Gmail帐户并过滤每个用户的传入电子邮件?

Kafka—性能逆天的存在

Kafka——性能逆天的存在