nodejs-服务的简单调优

有许多第三方工具可用来分析Node.js应用程序,比如火焰图。但在许多情况下,最简单的选择是使用内置的分析器使用V8内部的分析器,在程序执行期间定期对堆栈进行采样。它将这些示例的结果以及重要的优化事件记录为一系列滴答,本文假设读者已经对nodejs异步回调的特性有一点了解,不了解也不所谓,只是了解的话,文章阅读会更清晰。

下面介绍一下,使用nodejs内置的V8分析器,分析并优化一个web接口:

首先准备好web服务,我们使用express框架搭一个简单的接口服务,下面看代码:

const express = require("express");
const crypto = require("crypto");

const app = express();

app.get("/register",async(req, res)=>{
  // 获取传入的用户名和密码
  let username = req.query.username;
  let password = req.query.password;
  // 生成加强密码强度的盐值
  let slat = crypto.randomBytes(16).toString("base64")
  const hash = crypto.pbkdf2Sync(password,slat,10000,512,"sha512")
  // 将结果数据返回
  res.send(JSON.stringify({username,hash:hash.toString("base64")}));
});

app.listen(8080);

上面我们准备好了一个web服务,下面我们把这个服务跑起来,并用测试工具对它进行测试:

启动:node --prof app.js

// 使用--prof参数,表示使用内部的剖析器,使用这个参数启动应用后,当有请求访问时,会在应用应用程序当前目录下生成一个.log文件,这个文件里面记录了nodejs的堆栈信息

测试并发:ab -k -c 20 -n 250 "http://localhost:8080/register?username=matt&password=password"

// 使用上面的命令去请求我们启动好的服务,ab是linux下的一个并发测试工具,-c 表示并发数,-n 表示总共要完成多少个请求,ab的详细用法,这里不再解释,大家自行了解。

下面是我运行ab命令后的结果:

nodejs-服务的简单调优_第1张图片

下面是我本机生成的log文件的结果:

nodejs-服务的简单调优_第2张图片

这个log文件是不可读的,需要用nodejs转换为我们可分析的文件,使用下面的命令:

node --prof-process isolate-0xnnnnnnnnnnnn-v8.log > processed.txt

 转换后的文件内容有点多,我只粘贴出对我们分析有用的部分:

nodejs-服务的简单调优_第3张图片

上图给出的摘要部分:

可以看到99%的耗时都消耗在了C++对应的库,因为nodejs的加密模块底层是调用C++的库来实现的;javascript代码执行只占用了0.9%;剩余的GC(垃圾回收)占用0.1%,这也是一个需要关注的值;文中的例子比较简单,所以nodejs的垃圾回收在这个例子中占用的很少,当接口很复杂的时候,GC肯定会变大,当GC占用过多的时候,就要考虑对代码格式进行优化,比如声明过多的变量,导致GC明显增大;这是可以考虑使用变量复用,前面声明的变量生命周期已经结束来,就可以考虑在后面需要声明新变量的时候,复用前面的变量名,唯一的缺点就是,变量的复用,会使得代码可读性变差,这就需要详细的代码注视了。这样一定程度可以减小GC的占用,因为当GC运行的时候,程序是不能做其它任何事情的。

我们接着分析文件,既然C++代码占用这么多时间,我们来看它具体做了什么,转换后的log文件中有C++的详细部分:

nodejs-服务的简单调优_第4张图片

上图可以看到 ,PBKDF2这个函数占用了97%,说明C++代码耗时的地方就在这里。找到了程序主要耗时的地方,那么我们接下来对这一块进行改进。

原来,我们用的pbkdf2Sync这个方法进行加密获得hash,这是一个同步的方法,nodejs的优点是异步回调,我们用异步来充分发挥nodejs的特性,pbkdf2Sync对应有一个异步的方法,看下面的代码:

app.get("/register",async(req, res)=>{
  // 获取传入的用户名和密码
  let username = req.query.username;
  let password = req.query.password;
  // 生成加强密码强度的盐值
  let slat = crypto.randomBytes(16).toString("base64")
  // 使用异步来加密获得hash值
  crypto.pbkdf2(password,slat,10000,512,"sha512",(err,hash)=>{
       // 将结果数据返回
      res.send(JSON.stringify({username,hash:hash.toString("base64")}));
    });
});

我们再运行程序,然后使用ab测试工具,重新测试,看一下结果:

这次的运行结果每个请求16毫秒,之前是48毫秒,性能提高了2倍多;再一次证明,nodejs的异步回调的优势。

我们再来看滴答文件:

nodejs-服务的简单调优_第5张图片

这次,javascript代码占用为15.9%,GC占用1.3%,二者都有明显的上升,因为之前pbkdf2Sync同步方法,会阻塞后面的请求,改为异步后,在执行C++代码时,nodejs仍然可以接受后面的请求,待加密完成后,再响应前面的请求。从而提高了nodejs程序的性能。

你可能感兴趣的:(nodejs,nodejs调优)