我是如何把node接口耗时降低23%的

Node Time-Consuming

Posted by markzhang on April 21, 2019

在我所在的团队Node主要是用于提供接口数据和页面渲染。既然文章标题是和node接口耗时相关的,我先讲一下我们node是如何做接口数据提供的。

前端页面要想拿到数据,主要会经历三层,node->后端->核心数据。这三层都是属于内网调用,互相之间各有nginx进行调度。

所以从以上三层结构来看,想要提升node接口耗时,就是对内(node自身)处理逻辑和对外(后端及核心数据)交互过程进行优化。

由于node是单线程模型,所以优化更多的是要减少io操作,减少同步阻塞,避免执行CPU密集型操作,以下是我所使用的一些优化方法。

1、将串行网络请求改为并行网络请求,这一点非常重要,但有时候也容易被忽略,将网络请求改为并行之后会发现接口耗时下降明显。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 串行
let test = async () => {
  let res1 = await request(url1);
  let res2 = await request(url2);
  return [res1, res2];
};

// 并行
let test = async () => {
  let res = await Promise.all([
    request(url1),
    request(url2)
  ]);
  return res;
};

2、请求后端数据时采用持久连接

1
2
3
4
5
// 以下是axios的例子
let http = require('http');
let httpAgent = new http.Agent({keepAlive: true});
let axios = require('axios');
axios({httpAgent}).then(/******/).catch(/******/);

3、避免与后端做重复工作。由于历史原因,当初团队建立node项目迁移接口时会参考以前后端同学的代码,故而导致一些代码逻辑里面做了与后端同学一样的重复工作。从我们项目来看,发现比较多的情况是在node和后端都调用了公司登录校验的接口,在node层去掉一次校验之后,耗时大概下降了5-6ms。

4、node直连核心数据。在与后端同学沟通过程中发现一些接口在他们那边并没有逻辑处理,完全是透传的过程,所以决定重写node网络请求模块,使其支持node直连核心数据的方式,减少一层网络请求的过程,由原来的三层结构变为两层,经过处理后的接口耗时平均下降了8ms。

5、使用调试工具查找node运行时耗时比较高的操作

  • 启动node服务时加上–inspect参数(inspect前面是两根横杠)
  • 在chrome里面输入chrome://inspect,可以看到如下的页面,然后点击红色箭头处的inspect,弹出调试页面。
  • 在调试页面里面点击第二项Profiler,点击start
  • 使用压测工具对本地服务发送请求,我这里使用的工具是loadtest
  • 请求发送完毕之后,在inspect页面点击stop按钮,左边CPU PROFILES会出现一个CPU运行时记录文件,点击它之后右边会展开详细的node代码运行耗时列表。 经过以上步骤我们可以逐步找到代码中比较耗时的操作,然后可以查看代码逻辑进行针对性的优化,优化一些CPU密集型操作的代码,比如使用fast-json-stringify替代JSON.stringify()。

6、对不会频繁变动的接口数据进行缓存。在我们的项目里面有两种缓存方式,一种是将数据存储在机器内存里面,另一种是存储在redis中。我们分别使用的是memory-cache和ioredis工具包,如需使用,可参考官方文档。

7、升级node版本。node项目在第一次线上部署之后,版本就处于7.10.0一直没更换过;根据官网的benchmark(https://benchmarking.nodejs.org/)来看,node版本的升级还是会带来比较大的性能提升,因为7.10.0版本确实属于比较老的版本了。 以下是我的升级过程:

  • 先将本地版本进行升级,对页面渲染和接口运行进行测试,确保可以稳定运行;使用nvm进行版本切换,本地启动node服务,使用压测工具分别对两个版本的node页面渲染和接口响应速度进行测试。
  • 将测试环境版本进行升级,观察一周,看是否有报错情况。
  • 将线上一台机器进行升级,并让运维同学修改nginx配置进行流量控制,对这台机器一点一点的放量。
  • 观察一段时间,没有收到报障的反馈,将其他机器node全部进行升级。 我们这边是使用的pm2来启动的服务,以下是升级node版本的时候所使用的命令,仅供参考。
    1
    2
    3
    
    pm2 save
    pm2 kill
    pm2 resurrect
    

以上便是目前我对于降低node接口耗时所使用的一些小小的办法,如果你还有一些更好的想法和思考,欢迎联系我,一起交流学习。