zoukankan      html  css  js  c++  java
  • Node性能如何进行监控以及优化?

    一、 是什么

    Node作为一门服务端语言,性能方面尤为重要,其衡量指标一般有如下:

    • CPU
    • 内存
    • I/O
    • 网络

    CPU

    主要分成了两部分:

    • CPU负载:在某个时间段内,占用以及等待CPU的进程总数
    • CPU使用率:CPU时间占用状况,等于 1 - 空闲CPU时间(idle time) / CPU总时间

    这两个指标都是用来评估系统当前CPU的繁忙程度的量化指标

    Node应用一般不会消耗很多的CPU,如果CPU占用率高,则表明应用存在很多同步操作,导致异步任务回调被阻塞

    内存指标

    内存是一个非常容易量化的指标。内存占用率是评判一个系统的内存瓶颈的常见指标。对于Node来说,内部内存堆栈的使用状态也是一个可以量化的指标

    // /app/lib/memory.js
    const os = require('os');
    // 获取当前Node内存堆栈情况
    const { rss, heapUsed, heapTotal } = process.memoryUsage();
    // 获取系统空闲内存
    const sysFree = os.freemem();
    // 获取系统总内存
    const sysTotal = os.totalmem();

    module.exports = {
      memory: () => {
        return {
          sys: 1 - sysFree / sysTotal,  // 系统内存占用率
          heap: heapUsed / headTotal,   // Node堆内存占用率
          node: rss / sysTotal,         // Node占用系统内存的比例
        }
      }
    }
    • rss:表示node进程占用的内存总量。
    • heapTotal:表示堆内存的总量。
    • heapUsed:实际堆内存的使用量。
    • external :外部程序的内存使用量,包含Node核心的C++程序的内存使用量

    Node中,一个进程的最大内存容量为1.5GB。因此我们需要减少内存泄露

    磁盘 I/O

    硬盘的IO 开销是非常昂贵的,硬盘 IO 花费的 CPU 时钟周期是内存的 164000 倍

    内存 IO比磁盘IO 快非常多,所以使用内存缓存数据是有效的优化方法。常用的工具如 redismemcached

    并不是所有数据都需要缓存,访问频率高,生成代价比较高的才考虑是否缓存,也就是说影响你性能瓶颈的考虑去缓存,并且而且缓存还有缓存雪崩、缓存穿透等问题要解决

    二、如何监控

    关于性能方面的监控,一般情况都需要借助工具来实现

    这里采用Easy-Monitor 2.0,其是轻量级的 Node.js 项目内核性能监控 + 分析工具,在默认模式下,只需要在项目入口文件 require 一次,无需改动任何业务代码即可开启内核级别的性能监控分析

    使用方法如下:

    在你的项目入口文件中按照如下方式引入,当然请传入你的项目名称:

    const easyMonitor = require('easy-monitor');
    easyMonitor('你的项目名称');

    打开你的浏览器,访问 http://localhost:12333 ,即可看到进程界面

    关于定制化开发、通用配置项以及如何动态更新配置项详见官方文档

    三、如何优化

    关于Node的性能优化的方式有:

    • 使用最新版本Node.js
    • 正确使用流 Stream
    • 代码层面优化
    • 内存管理优化

    使用最新版本Node.js

    每个版本的性能提升主要来自于两个方面:

    • V8 的版本更新
    • Node.js 内部代码的更新优化

    正确使用流 Stream

    Node中,很多对象都实现了流,对于一个大文件可以通过流的形式发送,不需要将其完全读入内存

    const http = require('http');
    const fs = require('fs');

    // bad
    http.createServer(function (req, res) {
        fs.readFile(__dirname + '/data.txt', function (err, data) {
            res.end(data);
        });
    });

    // good
    http.createServer(function (req, res) {
        const stream = fs.createReadStream(__dirname + '/data.txt');
        stream.pipe(res);
    });

    代码层面优化

    合并查询,将多次查询合并一次,减少数据库的查询次数

    // bad
    for user_id in userIds 
         let account = user_account.findOne(user_id)

    // good
    const user_account_map = {}   // 注意这个对象将会消耗大量内存。
    user_account.find(user_id in user_ids).forEach(account){
        user_account_map[account.user_id] =  account
    }
    for user_id in userIds 
        var account = user_account_map[user_id]

    内存管理优化

    在 V8 中,主要将内存分为新生代和老生代两代:

    • 新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象
    • 老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象

    若新生代内存空间不够,直接分配到老生代

    通过减少内存占用,可以提高服务器的性能。如果有内存泄露,也会导致大量的对象存储到老生代中,服务器性能会大大降低

    如下面情况:

    const buffer = fs.readFileSync(__dirname + '/source/index.htm');

    app.use(
        mount('/', async (ctx) => {
            ctx.status = 200;
            ctx.type = 'html';
            ctx.body = buffer;
            leak.push(fs.readFileSync(__dirname + '/source/index.htm'));
        })
    );

    const leak = [];

    leak的内存非常大,造成内存泄露,应当避免这样的操作,通过减少内存使用,是提高服务性能的手段之一

    而节省内存最好的方式是使用池,其将频用、可复用对象存储起来,减少创建和销毁操作

    例如有个图片请求接口,每次请求,都需要用到类。若每次都需要重新new这些类,并不是很合适,在大量请求时,频繁创建和销毁这些类,造成内存抖动

    使用对象池的机制,对这种频繁需要创建和销毁的对象保存在一个对象池中。每次用到该对象时,就取对象池空闲的对象,并对它进行初始化操作,从而提高框架的性能

    本文来自博客园,作者:喆星高照,转载请注明原文链接:https://www.cnblogs.com/houxianzhou/p/15036006.html

  • 相关阅读:
    JavaWeb--HttpSession案例
    codeforces B. Balls Game 解题报告
    hdu 1711 Number Sequence 解题报告
    codeforces B. Online Meeting 解题报告
    ZOJ 3706 Break Standard Weight 解题报告
    codeforces C. Magic Formulas 解题报告
    codeforces B. Sereja and Mirroring 解题报告
    zoj 1109 Language of FatMouse 解题报告
    hdu 1361.Parencodings 解题报告
    hdu 1004 Let the Balloon Rise 解题报告
  • 原文地址:https://www.cnblogs.com/houxianzhou/p/15036006.html
Copyright © 2011-2022 走看看