当前位置: 首页 > 后端技术 > Node.js

Node性能指标(一)——内存分析与管理

时间:2023-04-04 00:53:46 Node.js

关于Node的前言JavaScript运行在浏览器的沙箱中,永远会受到浏览器中间层提供的能力的限制。Node技术的出现,为前端工作打开了一个新局面。毫无疑问,现代前端工程都离不开Node的应用,而Node本身就是被设计为作为服务端语言使用的。越来越多的前端团队不再将node局限于工程应用,也开始负责BFF层,比如SSR架构、数据适配、数据剪接、后端应用等。服务器应用,服务的【稳定性】和【安全性】是最重要的指标。提到“首屏加载时间”、“交互时间”等性能指标时,这些h5的性能,每个前端er指数都懂。Node作为服务器,有哪些性能指标值得我们关注?影响节点服务的因素节点作为服务器使用。相对于前端工程,我们不仅需要关注节点本身的特性,还需要关注依赖的服务器资源;如果依赖服务器的性能不好,必然会影响节点服务的性能;因此,我们在思考节点服务的性能时,需要从两大方面来思考:一是节点运行时会出现的问题,二是服务器资源CPU内存磁盘I/O的性能NetworkI/O本文主要分析内存指标,排查内存泄露隐患。内存限制。节点存储分为堆和栈。基本数据类型存放在栈中,引用类型存放在堆中:对象和变量;对于节点存储来说,堆内存是整个内存的主要占用。我们关心的内存指标是指堆内存占用指标。一般后端语言几乎没有内存限制。而node是基于V8引擎的,它遵循V8的方法进行对象分配。Node通过Javascript使用的内存是有限的。64位系统最大内存约1.4G,32位系统最大内存约0.7G;之所以有内存限制,一方面,V8本来就是为浏览器使用而设计的,这个限制的值对于一般的网页来说已经足够了。更深层次的原因是V8的垃圾回收机制;当我们声明一个变量并赋值时,它会存储在V8申请的堆内存中。当堆内存不够时,会继续申请内存,直到达到内存限制。如果超过限制,会出现内存泄漏、卡顿等现象;这里的内存指标是最方便量化的,可以通过节点数量提供process.memoryUsage()查看rss了解进程占用内存总量。heapTotal:堆内存申请总量。heapUsed:实际堆内存使用情况。\垃圾回收V8垃圾回收的基础是首先将内存进行分代;在V8中,内存是根据对象的生存时间来划分代数的。生存时间短的(可以立即回收的变量)放入新生代,常驻内存(全局变量,不能立即回收的变量)放入老年代;v8内存空间=新生代占用的内存空间+老年代占用的内存空间;node还提供了扩展内存的方法。在启动node的时候,可以通过--max-old-space-size和--max-old-space-size来调整内存的大小,对应expansion的值就是老年代内存的内存值和上面提到的新一代。此调整一旦开始,除非再次开始,否则无法更改。V8内存受限时,可以按这个值放宽node--max-old-space-size=1800server.js//即使调成1800Mb,我们也不能用完V8申请的所有内存,这个出处基于v8的回收策略目前的Node采用的是scavenge算法,基于复制实现垃圾回收,一定程度上浪费了内存资源;在代码开发过程中,也可以根据需要使用global.gc()主动触发垃圾回收。如果查询heapUsed在active触发后没有减少,可以认为存在内存泄漏。通过上面的分析,如果堆内存达到了堆内存的index,就不能用于new变量了。/对象申请新内存时,会发生内存泄漏。老年代的常驻内存不会被V8回收,即使是手动启动。代码中的几个常用项不会立即被垃圾回收,堆积过多会造成内存泄漏的隐患:全局变量指的是闭包作用域内的变量模块缓存内存泄漏的简单案例,仅供学习内存分析:每次请求时,都会将请求发送的信息发送到数据库中去取数据,并对读取到的数据做一层缓存。简单示例代码如下:const{json}=require('express');constexpress=require('快递');const{v4:uuid}=require('uuid');constapp=express();functiongetDataBase(){constcache={}returnfunction(key){if(cache[key])returncache[key]letdata=newArray(10000).fill('cache')cache[key]=数据返回data}}constdataBase=getDataBase()app.get('/memoryUsage',(req,res,next)=>{letuid=uuid()letdata=dataBase(uid)res.json({msg:'内存data',data:JSON.stringify({data})})})constport=3100app.listen(port,()=>{console.log(`在http://localhost:${port}监听的示例应用程序`)})通过wrk压测工具,会发现请求数一多,请求就挂了。现在是分析节点内存的时候了。下面是Chrome-Memory分析node.js调试工具:Chrome调试启动Chrome开发者工具:启动node服务时,通过--inspectnode--inspectserver.js打开chrome,地址中输入chrome://inspect字段,界面如下图所示,点击Target类型的节点服务,在弹出的弹框中选择Allocationsampling,点击start按钮开始录制。如果是单个请求,则没有问题。所有的问题都是在请求量大的时候使用压测工具向节点服务发送并发请求导致的。我这里使用wrk工具模拟高并发请求wrk-t12-c1000-d30shttp://localhost:3100/memoryUsage发送前的内存状态:发送后10秒左右,内存已经达到700多。停止后选择结束录音,会出现如下图所示的录音报告。可以看出99%的内存使用都在缓存的代码中。这样就可以分析出隐藏的内存代码。更多节点调试可以参考:https://www.ruanyifeng.com/bl...内存监控的实际应用。一个完善的节点服务的性能指标不仅仅是内存,包括cpu利用率、响应时间、状态码监控等。对于这些在线服务,社区中也有很多有用的工具。推荐一个非常简单易用的监控工具express-status-monitor。项目中引入该工具后,可以使用默认路由(/status),也可以使用指定路由。接入各种指标实时监控conststatusMonitor=require('express-status-monitor')({title:'XXX服务实时监控',spans:[{interval:1,//每15秒retention:100//在内存中保留60个数据点}]});app.use(statusMonitor)