websocket 的可怕性能问题 - 每条消息都会触发 Angular 变化检测 [英] Terrible performance issues with websocket - every message triggering Angular change detection

查看:80
本文介绍了websocket 的可怕性能问题 - 每条消息都会触发 Angular 变化检测的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

this.socket = new WebSocket('ws://localhost:5000/ws');让我 = 0;this.socket.onmessage = (e: MessageEvent) =>{我++;如果(我 % 100 === 0){console.log('收到' + i);}};

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

它每 100 条消息写入一次控制台.这仍然会以 100% 的 CPU 使用率杀死浏览器,它甚至在大多数情况下都不会输出到控制台,直到我停止消息流,然后一切都赶上了,它会喷出几行收到的 x00"消息.

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

<代码>{"主题":"2a736d15-a2fe-43b2-8e8b-ee888f15a53a","类型":1,信息": {价值":2,"时间戳":"2017-06-05T14:46:21.615062+01:00"}}

它们通常约为 126 个字符.

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

我还检查了 CPU 分析器.当一切都被锁定时,它只是一堵巨大的电话墙,所以我把它变成了一条消息:

深入研究这些峰值之一(每条消息都会发生!):

我在其中一个位置设置了一个断点,看来每当 websocket 被触发时 Angular 都会做一些事情,这与 Zones 有关(我在这方面的经验为零!):

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

这一切怎么会发生?订阅websocket的7行代码完全独立于Angular的变化检测,对吗?它们不会更改绑定到任何组件的任何值.它们唯一的联系是它们位于 @Injectable()

内的方法中

解决方案

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

这是区域的问题.这是一篇有用的博文.一般来说,Angular 2 使用区域来驱动它的变化检测:

<块引用>

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

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

this.zone.runOutsideAngular(() => {让我 = 0;this.socket.onmessage = (e: MessageEvent) =>{我++;如果(我 % 100 === 0){console.log('收到' + i);}};});

I've got a websocket and I'm currently streaming messages at about 45 per second. At this level it completely kills chrome. All functionality in the browser window is locked up.

I've cut back my implementation to try find the root cause of this issue, suspecting it could have been some problem with how I'm processing these messages - or updating/change detection in Angular.

But now I'm left with the simplest websocket implementation I can get:

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);
  }
};

This is in an Angular Injectable, but it's not interacting with anything.

It writes to the console every 100 messages. This still kills the browser with 100% CPU usage, it doesn't even output to the console most of the time until I stop the message stream, then everything catches up and it squirts a few lines of "recieved x00" messages.

The messages themselves are JSON, and look like this:

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

They're typically about 126 characters.

I'd assumed websockets can handle tens of thousands of messages per second, but I can't find any sensible metrics on google, does this sound like unreasonable performance?

I've also checked in the CPU profiler. When everthing's locked out, it's just a big wall of calls, so I turned it down to a single message a second:

Drilled down into one of these spikes (happens for every single message!):

I threw a break point in one of these locations, and it appears that Angular is doing something whenever the websocket gets triggered, and it's something to do with Zones (something I have zero experience in!):

The first thing in the stack is a ZoneTask.invoke on the Websocket itself, but then NgZone gets triggered, and ultimately a very expensive change/update chain is called:

How could all this be happening? The 7 lines of code subscribing to the websocket are completely independent from Angular's change detection right? They're not changing any values that are bound to any components. Their only link is they are sitting in a method inside an @Injectable()

解决方案

Sods law, as soon as I post a question I work out the answer. Usually I'd delete it but I couldn't find much on the topic so it might be useful for someone.

It was an issue with Zones. Here's a useful blog post. In general, Angular 2 uses zones to drive it's change detection:

The reason that Angular 2 uses the zone.js is to know when our handler finishes and then the NgZone service (NgZone is a class that wrap the zone service) calls to ApplicationRef.tick() method. The tick() method scans the tree components from top to bottom and calculates in each component the expressions that exist in the template. If the result of the expression is not equal to the previous result, (from the previous tick) Angular will update the DOM property that connects to this expression.

An easy solution to simply get the message performance in isolation is to run outside of the zone, and thus angular's change detection with the handy method runOutsideAngular:

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

这篇关于websocket 的可怕性能问题 - 每条消息都会触发 Angular 变化检测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆