服务稳定性到一定程度之后,都会开始经历一段精细化运营的过程,从成本意识角度来说也是成立的。作为前端出身的NodeJS开发者们,产生共鸣的那就是如何能够直观且快速发现性能瓶颈,能够像调试前端的JS代码那样可视化,堆栈化,接下来我们就针对常见的CPU性能分析方法来揭开NodeJS的CPU面纱。
充分利用劳动工具有助于帮助我们提升定位问题的效率,Linux kernal自带的系统性能分析工具perf,为我们提供函数级与指令级的热点查找,常用于性能瓶颈的查找与热点代码定位。
#NodeJS如何正确完整的采集火焰图呢?
1-1、用例构造
NodeJS服务代码示例—JSON编解码,如下片段:
1-2 启动方式:
启动参数:--perf_basic_prof或—perf-basic-prof
适用于node@0.11.13
也可以使用--perf_basic_prof_only_functions
标准方式:node --perf_basic_prof JsonParse.js
MIG tafNodeJS启动方式:私有模板配置启动参数,如下:
<taf>
<application>
<server>
node-args=--perf-basic-prof
</server>
</application>
</taf>
注:--perf_basic_prof或--perf-basic-prof
会对应生成一个/tmp/perf-pid.map
文件,如图:
1-3 perf脚本采集函数和热点代码
#查看NodeJS服务进程pid,采集时需要用到。命令:$ ps –ef | grep ‘worker’
#采集脚本:$ perf record -F 99 -p 182497 -g -- sleep 60
参数说明如下:
采集频率(ms) | 进程pid | 调用记录 | 记录时长 |
---|---|---|---|
-F 99 | -p 182497 | -g | --sleep 60 |
#进程对应的符号表perf-pid.map权限设置:$chown root /tmp/perf-pid.map
。
踩过的坑!!!是为了消除下面这个问题的
File /tmp/perf-PID.map not owned by current user or root, ignoring it (use -f to override).
Failed to open /tmp/perf-PID.map, continuing without symbols
http://github.com/brendangregg/FlameGraph
单一颜色:$ perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > xxx.svg
多种颜色:perf script | ./stackcollapse-perf.pl | ./flamegraph.pl --color=js –hash > xxx.svg
,如下图:
2.1 通过上面的步骤采集出两种不同颜色系的火焰图,如下图
2.2 火焰图颜色对应关系,如下表:
颜色 | 类型 |
---|---|
绿色 | JS代码调用 |
蓝色 | 优化编译代码 |
黄色 | C++/C代码 |
红色 | libuv系统调用 |
2.3 火焰图形状对应关系
形状 | 含义 |
---|---|
每一个平面方块 | 一个函数在栈中的位置(也称一个栈帧) |
Y轴 | 栈的深度(也叫栈的帧数) |
X轴 | 表示总的样例,不过它们左右顺序没有特殊含义 |
每个平面方块的宽度 | 方块的宽度标示CPU使用时间或者说相对父函数而言使用CPU的比率,越宽代表占用CPU的时间越长,或者使用CPU很频繁 |
2.4 JSON序列化与反序列化火焰图分析
采用ab进行压力测试分析JSON.parse与JSON.stringify性能开销
压测命令:ab –n 3000000 -c 50 http://ip:port/jsonParse
从火焰图看到JsonParse.js里面耗时主要消耗在JSON序列化和反序列化
几个常见的栈帧类型说明:
栈帧 | 含义 |
---|---|
LazyCompile | 指的是下回会被编译 |
Builtin | 指的是C++内置的运算方法 |
Stub | C入口桩代码:作用是在js的JIT代码中,如果要调用Runtime的函数,则通过CEntryStub实现 |
V8::internal | 内部命名空间,就是C++的namespace,在V8的源代码可以找到对应的namespace |
2.5 JSON反序列化(JSON.parse)源码分析
概念普及——解析器常见原理:
a) 词法分析
b) 语法分析生成抽象语法树(AST)
c) 针对抽象语法树进行语义分析,构建你需要的内部数据结构或生成代码
通过局部查看火焰图分析源码
1 Stub:CEntryStub:C入口桩代码,在JS的JIT代码中,提供调用Runtime的函数(如DOM函数或者JS的builtin函数)
2【编译builtins.h】v8::internal::Builtin_JsonParse
3【解析器json-parser.cc】v8::internal::JsonParser<true>::ParseJson
4【词法分析json-parser.cc】v8::internal::JsonParser<true>::ParseJsonValue
5【语法分析json-parser.cc】v8::internal::JsonParser<true>::ParseJsonObject
小结:通过火焰图我们能够清晰的看到函数的调用栈,并能够找到哪些函数是耗时较多的
JSON序列化流程相似相似,感兴趣的同学可以看一下V8的json-stringifier.cc和对应的.h文件
3.1 需要gprof2dot脚本
工程地址:https://github.com/jrfonseca/gprof2dot.git
3.2 基于perf.data文件转换成dot文件
$ perf script -i perf.data.pid | python gprof2dot.py –f perf –e 1 –o xxx.dot
3.3 基于perf.data文件转换成dot文件
$ dot –Tsvg xxx.dot –o xxx.svg
在linux物理机的尝试:
下载到PC磁盘,打开即可:
https://zhuanlan.zhihu.com/p/27147421
https://www.zhihu.com/question/24640264
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。