我们知道 Node.js 是基于 V8 引擎的,V8 暴露了一些 profiler API,我们可以通过 v8-profiler 收集一些运行时数据(例如:CPU 和内存)。本节将介绍如何使用 v8-profiler 分析 CPU 的使用情况。
创建测试代码:
app.js
const fs = require('fs') const crypto = require('crypto') const Bluebird = require('bluebird') const profiler = require('v8-profiler') const Paloma = require('paloma') const app = new Paloma() app.route({ method: 'GET', path: '/encrypt', controller: function encryptRouter (ctx) { const password = ctx.query.password || 'test' const salt = crypto.randomBytes(128).toString('base64') const encryptedPassword = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex') ctx.body = encryptedPassword }}) app.route({ method: 'GET', path: '/cpuprofile', async controller (ctx) { //Start Profiling profiler.startProfiling('CPU profile') await Bluebird.delay(30000) //Stop Profiling after 30s const profile = profiler.stopProfiling() profile.export() .pipe(fs.createWriteStream(`cpuprofile-${Date.now()}.cpuprofile`)) .on('finish', () => profile.delete()) ctx.status = 204 }}) app.listen(3000)
GET /encrypt 有一个 CPU 密集型的计算函数 crypto.pbkdf2Sync,GET /cpuprofile 用来收集 30s 的 V8 log 然后将其 dump 到一个文件中。
GET /encrypt
GET /cpuprofile
运行该程序,打开两个终端窗口。一个终端运行:
$ curl localhost:3000/cpuprofile
来触发 CPU profiling,然后另一个终端立即运行:
$ ab -c 20 -n 2000 "http://localhost:3000/encrypt?password=123456"
来触发 CPU 密集计算。
最后生成 cpuprofile-xxx.cpuprofile 文件,该文件的内容其实就是一个大的 JSON 对象,大体如下:
{ "typeId": "CPU", "uid": "1", "title": "CPU profile", "head": { "functionName": "(root)", "url": "", "lineNumber": 0, "callUID": 154, "bailoutReason": "", "id": 1, "scriptId": 0, "hitCount": 0, "children": [ ... ] }, "startTime": 276245, "endTime": 276306, "samples": [ ... ], "timestamps": [ ... ] }
这个 JSON 对象记录了函数调用栈、路径、时间戳和一些其他信息,samples 节点数组与 timestamps 节点数组中的时间戳是一一对应的,并且 samples 节点数组中的每一个值其实对应了 head 节点的深度优先遍历 ID。这里我们不深究每个字段的含义,先来看看如何可视化这些数据。
Chrome 自带了分析 CPU profile 日志的工具。打开 Chrome -> 调出开发者工具(DevTools) -> 单击右上角三个点的按钮 -> More tools -> JavaScript Profiler -> Load,加载刚才生成的 cpuprofile 文件。左上角的下拉菜单可以选择如下三种模式:
这里我们选择 Tree (Top Down) 模式,按 Total Time 降序排列。可以看到有如下三列:
我们不断地展开,并定位到了 encryptRouter,如下图所示:
可以看出:我们定位到了 encryptRouter 这个路由,并且这个路由中 exports.pbkdf2Sync 占据了绝大部分 CPU 时间。
我们也可以用火焰图来展示 cpuprofile 数据。首先全局安装 flamegraph 模块:
$ npm i flamegraph -g
运行以下命令将 cpuprofile 文件生成 svg 文件:
$ flamegraph -t cpuprofile -f cpuprofile-xxx.cpuprofile -o cpuprofile.svg
用浏览器打开 cpuprofile.svg,如下所示:
可以看出:我们定位到了 app.js 的第 8 行,即 encryptRouter 这个路由,并且这个路由中 exports.pbkdf2Sync 占据了绝大部分 CPU 时间。
v8-analytics 是社区开源的一个解析 v8-profiler 和 heapdump 等模块生成的 CPU 和 heap-memory 日志的工具。它提供以下功能:
我们以上述第 2 个功能为例,使用 v8-analytics 分析 CPU 的使用情况。
首先,全局安装 v8-analytics:
$ npm i v8-analytics -g
使用以下命令查看执行时间大于 200ms 的函数:
$ va timeout cpuprofile-xxx.cpuprofile 200 --only
结果截图如下:
可以看出:我们依然能够定位到 encryptRouter 和 exports.pbkdf2Sync。
上一节:1.1 perf + FlameGraph
下一节:1.3 Tick Processor
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8