nodejs项目性能监控与分析与优化

 

1 人赞同了该文章
展开目录
 
在应用层,直观的资源指标就是cpu和内存,底层交互的是句柄,nodejs基于事件循环,一个重要的性能指标就是事件循环时间

一、性能指标输出

自己懒得探索的直接使用pm2的性能指标,把pm2学好也可以
setInterval(() => {
 // 为了优化 Node.js 应用的性能,开发者应该尽量减少事件循环中的阻塞操作,避免在事件循环的某个阶段执行耗时较长的任务。此外,合理使用微任务宏任务也可以帮助提高应用的响应速度
 const startTime = process.hrtime()
 setImmediate(() => { 
  const endTime = process.hrtime(startTime)
  const delayInNanoseconds = endTime[0]*1e9 + endTime[1]
  const delayInMilliseconds = delayInNanoseconds / 1e6
  console.log(`Event loop delay : ${delayInMilliseconds.toFixed(2)} ms`)
 }

 // 在Node.js中,“句柄”(handle)是一个广泛的概念,它通常指的是一个引用或指针,用于标识和访问某种资源或对象。这些资源可以是文件、网络套接字、定时器、进程等。Node.js提供了各种API来创建和管理这些句柄,使开发者能够高效地与底层系统进行交互
 const activeHandles = process._getActiveHandles() 
 let socketNum = 0
 activeHandles.forEach(v => {
  const name = v.constructor.name
  if(name === 'Socket') socketNum++
 })
 console.log(`jubingshu: ${activeHandles.length}  sockets: ${socketNum}`)

 // cpu和内存
 console.log(process.cpuUsage(),process.memoryUsage().heapTotal/1024/1024,process.memoryUsage().heapUsed/1024/1024) 
},2000)

二、性能优化

1. 算法优化

在Node.js中优化算法通常涉及多个方面,包括代码效率、内存使用、以及选择合适的数据结构和算法来解决问题。以下是一些常见的优化技巧和建议:

1. 选择合适的数据结构
‌数组(Arrays)‌:适用于需要频繁访问元素的场景,但插入和删除操作可能较慢(尤其是数组中间的操作)。
‌链表(Linked Lists)‌:适用于需要频繁插入和删除元素的场景,但随机访问元素较慢。
‌哈希表(Hash Tables)‌:适用于需要快速查找、插入和删除键值对的场景。
‌树(Trees)‌:如二叉搜索树(BST)、红黑树等,适用于需要有序存储和快速查找的场景。
2. 优化算法复杂度
‌时间复杂度(Time Complexity)‌:尽量选择时间复杂度较低的算法。例如,使用二分查找(O(log n))替代线性查找(O(n))。
‌空间复杂度(Space Complexity)‌:考虑算法的内存使用情况,避免不必要的内存开销。

分析和调试
‌性能分析‌:使用性能分析工具(如Node.js内置的Profilerclinic.js等)来找出代码中的性能瓶颈。
‌调试‌:使用调试工具(如Node.js内置的Debugger、VSCode调试器等)来逐步检查和优化代码。
示例:优化数组求和

2. 代码优化

1. 使用异步I/O
  Node.js是基于事件驱动和非阻塞I/O的,因此充分利用异步操作是提升性能的关键。避免使用同步函数,如fs.readFileSync,而应使用异步版本,如fs.readFile,配合回调、Promise或async/await。
2. 减少不必要的计算
  缓存计算结果,避免重复计算。
  使用高效的数据结构和算法。
  推迟计算,仅在需要时进行(懒加载)。
3. 优化循环和条件判断
  尽量减少循环次数,优化循环体内的操作。
  使用break、continue等语句提前退出循环或跳过不必要的迭代。
  简化条件判断,避免复杂的逻辑。
4. 使用内置的高效方法
  Node.js提供了许多内置的高效方法和库,如Buffer用于二进制数据处理,crypto用于加密操作等。尽量利用这些内置功能,而不是自己实现类似的功能
  ‌使用一些第三方库(如Lodash、Ramda等)提供了高效的数据处理函数,可以考虑使用。
5. 并发
  ‌对于计算密集型任务,可以使用worker_threads模块来创建多线程,提高执行效率。

3. 系统架构层面的优化

1. 集群模式
Node.js是单线程的,但可以通过cluster模块创建多个子进程来充分利用多核CPU的性能。每个子进程都是一个独立的Node.js实例,它们之间可以共享相同的端口。
2. 负载均衡
使用反向代理服务器(如Nginx)或Node.js自带的负载均衡功能来分发请求,避免单个实例成为性能瓶颈。
3. 微服务架构
将应用程序拆分为多个小型、独立的服务(微服务),每个服务负责处理特定的业务逻辑。这样可以提高系统的可扩展性和可维护性,同时也有助于性能优化。

4. 运行环境层面的优化

1. 使用最新的Node.js版本
Node.js不断更新,新版本通常包含性能改进和bug修复。因此,尽量使用最新的稳定版本。
2. 优化V8引擎参数
Node.js基于V8 JavaScript引擎,可以通过调整V8的参数来优化性能。例如,可以使用--max-old-space-size参数来增加V8的内存限制。
3. 使用性能分析工具
使用Node.js内置的性能分析工具(如Profiler、Inspector)或第三方工具(如clinic.js、0x.js等)来找出性能瓶颈并进行优化。
4. 监控和报警
设置监控系统来实时跟踪应用程序的性能指标(如CPU使用率、内存占用、响应时间等),并配置报警机制以便在性能异常时及时采取措施

5. 数据库层面的优化

1. 使用连接池
对于数据库连接,使用连接池来管理连接的生命周期,避免频繁创建和销毁连接带来的开销。
2. 优化查询
使用索引来加速查询操作。
避免使用SELECT *,只选择需要的字段。
使用分页查询来减少一次查询返回的数据量。
3. 缓存数据
对于频繁访问的数据,可以使用缓存(如Redis、Memcached等)来减少数据库访问次数。

三、可靠性实现

在服务器架构方面:
  如果计算资源充足,做弹性伸缩,就能保证大小流量的高质量服务
  如果计算资源有限,那么保证服务部崩就得使用nginx反向代理对流量最大值进行控制
在进程方面:
  使用pm2进程管理器或者docker容器部署保证程序不挂
  监控服务器硬件参数,发现异常向开发人员发送邮件,修复问题
  

四、多线程开发

多线程模块能使用多核进行cpu密集运算
// 主线程代码
const {Worker} = require('worker_threads')
const l = console.log

function Thread(workerData){
  const worker = new Worker('./worker.js',{workerData})

  worker.on('message',msg => l(`${workerData.mark}号 `,String(msg).length))
  worker.on('error',error => l(2,error))
  worker.on('exit',code => l(3,code))
}

for (let i = 0; i < 10; i++) {
  Thread({num:Math.round(Math.random()*1e6),mark:i+1})
}


// 工作线程代码
const {parentPort, workerData} = require('worker_threads')

console.time(workerData.mark)
// 斐波那契数列计算
function fbnq(num){
  if(!(num > 2)){
    console.timeEnd(workerData.mark)
    return parentPort.postMessage(num)
  }
  let one = BigInt(0)
  let tow = BigInt(1)
  let current = 0
  while(num > 2){
    current = one+tow
    one = tow
    tow = current
    num--
  }
  console.timeEnd(workerData.mark)
  parentPort.postMessage([num,current])
}
fbnq(workerData.num)

 

编辑于 2025-01-13 14:50・北京

posted on 2025-09-09 17:23  漫思  阅读(12)  评论(0)    收藏  举报

导航