node 性能监控,js性能监控

  node 性能监控,js性能监控

  为什么需要性能监控?让我们看看本文中的Node.js性能监控。希望对你有帮助!

  node.js速度课程简介:进入学习

  

为什么需要性能监控

   Node作为Javascript在服务器端的运行时,极大的丰富了Javascript的应用场景。

  但是Node.js运行时本身就是一个黑盒,我们的运行时状态是无法感知,在线问题也是难以复现

  因此,性能监控是Node.js应用程序“正常运行”的基石。不仅可以随时监控运行时的各种指标,还可以帮助排除异常场景。

  

组成部分

  性能监控可分为两部分:

  绩效指标的收集和显示

  进程级数据:CPU、内存、堆、GC等系统级数据:磁盘占用率、I/O负载、TCP/UDP连接状态等应用级数据:QPS、慢速HTTP、业务处理链路日志等性能数据的捕获和分析。

  堆快照:堆内存快照Cpuprofile:CPU快照核心转储:应用程序崩溃快照

方案对比

  从上图可以看出三种主流Node.js性能监控方案的优缺点。下面简单介绍一下这三种方案的构成:

  普罗米修斯

  Prom-client是prometheus的nodejs实现,用于收集性能指标。grafana是一个显示各种数据图表的可视化平台。对prometheus的访问只支持性能指标的采集和显示,需要其他快照工具来排查问题,从而形成闭环AliNode。

  Alinode是与官方nodejs兼容的扩展运行时,它提供了一些附加功能:

  v8的运行时内存状态监控libuv运行时状态监控在线故障诊断功能:堆快照、CPU概要、GC Trace等。agenthub是一个常驻进程,用于收集和报告性能指标。

  集成agentx commdx的便捷工具,从监控、显示、快照、分析形成闭环,访问方便简单,但扩展运行时还是有风险。

  易于监控

  Xprofiler负责实时运行时状态采样,并输出性能日志(即捕获性能数据)。xtransit负责性能日志的收集和传输。Xprofiler和AliNode最大的区别就是用Node.js Addon来实现采样器

性能指标

  

CPU

  。

  当前进程的CPU耗时数据可以通过process.cpuUsage()获取,返回值的单位是微秒。

  用户:流程执行系统消耗的CPU时间:流程执行过程中系统消耗的CPU时间

Memory

  当前进程的内存分配数据可以通过process.memoryUsage()获取,返回值的单位是字节。

  Rss:内存驻留,由节点进程分配的总内存大小heap total:由V8应用的堆内存大小heap used:由V8外部使用的堆内存大小:由V8管理的C占用的内存大小arrayBuffers:分配给ArrayBuffer的内存大小

  从上图可以看出,rss包含代码段、栈和堆。

  代码段:存储代码段堆栈:存储局部变量和管理函数调用堆:存储对象、闭包或其他任何东西

Heap

   V8堆内存和堆空间的分析数据可以通过v8.getHeapStatistics()和v8.getHeapSpaceStatistics()获得。下图显示了v8堆内存的组成和分布:

  堆内存空间先分空间,空间分页面。根据1MB对齐方式对内存进行分页。

  新空间:新生代空间,用于存储一些生命周期较短的对象数据,分为两个空间(空间类型为半空间):from space和to space。

  提升条件:在新空间依然存活两次GC。旧空间:旧代空间用于存储在新空间中提升的对象。

  代码空间:存储v8 JIT编译的可执行代码。

  存储该对象所指向的隐藏类的地图空间指针对象。隐藏类指针是v8在运行时记录的对象布局结构,用于快速访问对象成员。

  大对象空间:用于存储大于1MB且无法分配给页面的对象。

  

GC

   v8垃圾收集算法分为两类:

  主要的GC: Mark-Sweep-Compact算法用于老一代的对象恢复。次要GC: Scavenge算法用于新一代的对象恢复

Scavenge

  前提:新空间分为两个对象空间,from和to。

  触发时间:当新空间已满时

  步骤:

  在“从空间”中,首先遍历宽度。

  发现一个活的(可触及的)物体

  幸存了一次(经历了一次扫荡),晋升到旧空间,其他人复制到空间。当复制完成时,只有活的物体进入空间,而从空间被清空。

  从空间交换到空间,开始下一轮的拾荒。

  适用于回收频繁,内存不大的对象,典型的空间换时间的策略,缺点是浪费了多一倍的空间

  

Mark-Sweep-Compact

  三个步骤:标记、清除和分类。

  触发时间:旧空间已满时

  步骤:

  标记(三色标记法)

  白色:代表可回收的对象黑色:代表不可回收的对象,由它们生成的所有引用都已被扫描;灰色:表示不可循环的对象,由它们生成的引用还没有被扫描。将V8根对象直接引用的对象放入标记队列(显式堆栈),并将这些对象标记为灰色。从这些对象开始,进行深度优先遍历。每次访问一个对象,将该对象弹出标记队列,标记为黑色,然后将该对象引用的所有白色对象标记为灰色,并将其推送到标记队列,以此类推,直到堆栈上的所有对象都弹出。旧物件只剩下两种:黑色(不可回收)和白色(可回收)。PS:当一个对象太大而无法被推到空间有限的堆栈中时,V8会以灰色跳过这个对象,将整个堆栈标记为溢出,在堆栈清空后,再次标记,这就导致需要再扫描一次堆扫。

  清除白色对象会造成不连续的内存空间压缩。

  Sweep会造成内存空间不连续,不利于新对象进入GC。把黑(活)物移到旧空间的一端,这样被清理出来的空间就是连续完整的。虽然可以解决内存碎片问题,但是会增加暂停时间(执行速度慢)。mark-compact

Stop-The-World

  仅在空间不足以分配新一代提升的对象时使用。在v8的垃圾收集开始时,需要停止程序并扫描整个堆。这种行为叫做停世。

  新生代活动天体虽然小,回收频繁,完全停止,影响不大,但老的有很多大的残存天体,标记、清理、整理造成的停顿会更严重。

  

优化策略

  增量标记:在标记阶段,当堆达到一定大小时,启动增量GC。每次分配一定的内存后,程序暂停,标记几毫秒到几十毫秒,然后程序恢复。这个想法其实有点像React框架中的纤程架构。浏览器只有空闲的时候才会遍历纤程树来执行相应的任务,否则会延迟执行,尽量少影响主线程的任务,避免应用堵塞,提高应用性能。

  并发清扫:让其他线程同时进行清扫,不用担心与执行程序的主线程发生冲突。并行扫掠:让多个扫掠线程同时工作,提高了扫掠的吞吐量,缩短了整个GC:

空间调整

  的周期因为v8默认限制了新老两代的大小。

  新空间的默认限制:64位系统32M,32位系统16m,64位系统1400M,32位系统700M。因此,node提供了两个参数来调整新老两代的空间上限。

  -max-semi-space-size:设置新空间的最大值-max-old-space-size:设置旧空间的最大值

查看GC日志

  节点还提供了三种查看GC日志的方式:

  -trace _ GC:日志的一个AliNode简要描述每个GC的时间、类型、堆大小变化以及原因-trace _ GC _ verbose:显示每个GC后每个V8堆空间的详细状态-trace _ GC _ NVP:每个GC的详细键值对信息,包括GC类型、暂停时间、内存变化等。由于GC日志比较原始,需要二次处理,所以可以使用。

  

快照工具

  

Heapsnapshot

  对堆内存个正在运行的程序进行快照采样,可以用来分析内存消耗和变化。

  

生成方式

  生成. heapsnapshot文件有几种方法:

  使用heapdump

  使用v8的堆概要文件

  使用nodejs内置的v8模块提供的api。

  v8.getHeapSnapshot()

  v8.writeHeapSnapshot(文件名)

  使用v8-profiler-next

  的。

分析方法

  生成的heapsnapshot文件可以上传到Chrome devtools工具栏的内存中,显示结果如下:

  默认视图是Summary视图,这里我们应该注意最右边的两列:should size和Retained Size。

  浅大小:表示v8堆中对象本身的内存分配大小。保留大小:表示该对象的所有被引用对象的浅大小之和。当发现保留的大小特别大时,对象内部可能存在内存泄漏,这可以进一步扩大重定位的问题。

  比较视图用于比较分析两个不同时期的堆快照,通过Delta列可以筛选出内存变化最大的对象。

  

Cpuprofile

  快照采样CPU个正在运行的程序,可以用来分析CPU时间消耗和比例。

  

生成方式

  生成. cpuprofile文件有几种方法:

  V8-profiler(Node提供的官方工具,但不支持node v10以上版本,不再维护)v8-profiler-next(维护中文版,支持到最新的node v18,正在持续维护中)。这是收集了5分钟的CPU配置文件示例。

  

分析方法

  已生成。cpuprofile文件,可以在Chrome devtools工具栏的Javascript Profiler中显示(不在默认选项卡中,需要在工具栏右侧的More中打开)。选择上传文件后,显示结果如下:

  默认视图是Heavy视图,这里我们看到有两列:Self Time和Total Time。

  Self Time:表示这个函数本身的执行时间(不包括其他调用)Total Time:表示这个函数的总执行时间(包括其他调用函数)。当发现总时间与自身时间偏差较大时,该函数的CPU密集型计算可能会比较耗时,可能会进行进一步的位置调查。

  

Codedump

  当应用程序意外崩溃终止时,系统会自动记录进程崩溃瞬间的内存分配信息、程序计数器、堆栈指针等关键信息,生成核心文件。

  

生成方式

  三种生成方式。核心文件:

  Ulimit -c unlimited打开内核限制Node-abort-on-un capture-exception Node开始添加这个参数,它也可以在应用程序中发生未捕获的异常时生成一个核心文件。gcore pid在获取。核心文件,实际进程崩溃的原因可以通过mdb、gdb、lldb等工具进行分析和诊断。

  llnode“哪个节点”- c/path/to/core/dump

分析方法

  

案例分析

  从监控中可以观察到堆内存不断上升,因此需要堆快照进行故障排除。

  

观察

  根据heapsnapshot可以分析出有一个newThing对象保持了比较大的内存。

  

分析

  从代码中可以看出,虽然没有调用未使用的方法,但是newThing对象是从theThing引用的,这导致它存在于replaceThing函数的执行上下文中,没有被释放。这是一个典型的闭包导致内存泄漏的例子。

  

排查

  常见的内存泄漏如下:

  全局变量closure timer事件监控缓存因此,在上述情况下,我们必须仔细考虑对象是否会在内存中自动回收。如果不会自动回收,需要手动回收,比如手动将对象设置为null,移除定时器,解除事件监控的绑定等。

  

小结

  至此,本文详细介绍了Node.js的整个性能监控系统。

  首先介绍了性能监控所要解决的问题,性能监控的组成以及主流方案的优缺点。

  然后,详细介绍了两个主要的性能指标和快照工具。

  性能指标主要关注CPU、内存、堆空间和GC。同时介绍了v8的GC策略和GC优化方案。快照工具主要有堆快照、CPU快照、崩溃时的Coredump。最后,通过观察、分析和故障排除,再现了一个简单的内存泄漏案例,并总结了常见的内存泄漏和解决方法。

  希望这篇文章能帮助你了解Node.js的整个性能监控系统

  更多关于node的信息,请访问:nodejs教程!这就是为什么需要性能监控。说说Node.js性能监控的详细内容。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: