第1页
@flyinweb 占超群[离哲]
NodeJs 应用 性能分析优化 & 分布式设计
第2页
提纲
•从实例开始 •性能分析&优化方法
•资源占用分析 •CPU、内存 •文件IO、网络IO
•慢代码分析 •V8 掠影
•内存、堆栈、GC、预编译 •分布式设计
•单机 •集群
大 海
捞 针
第3页
性能分析和优化
性能分析流程、工具、方法、优化要点
第4页
从实例开始
client
web Server
Redis Server
Interface Server
1:发起请求 NET IO
CPU
2:缓存检查
NET IO
3:数据交换和记录
4:记日志
NET IO
CPU
5:回应 NET IO
FILE IO
第5页
工具
• Linux tools
– pidstat/iostat/vmstat – sar/top/lsof
• node lib
– v8-profiler – Benchmark.js
• V8 tools
– node-v0.6.2/deps/v8/tools
• linux-tick-processor • ll_prof.py • run-valgrind.py
第6页
CPU 占用资源分析
利用率: 用户进程/内核/中断/IO等待/空闲 us / sy/(hi/si)/wa /id top
建议值: usr/sys:65%-75% / 30%-35%
分析: top (1->shift+h)
第7页
CPU 占用资源分析
• pidstat -p 1651 -t 1 100
node 应用 |-node 主线程 |-SignalSender线程 profile sampling |-4个libuv线程 iowatcher etc
第8页
CPU 优化/利用点
• 代码适应 V8 • 减少GC • 多进程 • 原生代码 • 模板选型 • 复杂计算业务逻辑转移
– Java/C app – Gearman类 任务分发(异步化) – MQ
• 语言层面
– eval – setInterval/setTimeout (arg) – Primitive operations – Regexp – async+parallel – Object constructor – Array Pre-allocate – 精简解析(httpparser)
第9页
文件IO 占用资源分析
• pidstat -d -p 1651 -t 1 100
– kB_rd/s kB_wr/s kB_ccwr/s
• iostat -x vda2 3 5
– %util – await
• sar –b
– rtps / wtps / (bread/s) / (bwrtn/s)
• 优化点
– IO分散 – stream读取大文件 – async – unwatcher
第10页
网络IO占用分析
• sar –n DEV 1 10
– IFACE rxpck/s txpck/s rxkB/s
• sar –n SOCK 1 10
– tcpsck udpsck
• sar –n TCP 1 10
– iseg/s oseg/s
• 优化点:
– maxsockets – timeout – response header(expire ..) – request (no cookies..) – pool – sync/async ( getaddrinfo/ gethostbyname /
ares_gethostbyname) – 分段读取 – 压缩传输(msgpack/bin/gzip)
第11页
内存占用
• free/vmstat
– Cached/buffered/swpd
• sar –B 1 5
– (pgpgin/s) / (pgpgout/s) / (pgscank/s) – (pgscand/s) /(pgsteal/s) /(%vmeff)
• sar -r 1 5
– Kbmemfree+kbbuffers+kbcached
• pidstat –r –p 1813 1 10
– minflt/s majflt/s VSZ RSS
• pidstat –s –p 1813 1 10
– minflt/s majflt/s VSZ RSS
第12页
内存占用
• 优化点:
– 整体
• 加入 Buffer(堆外内存) • 加大最大内存设置
– --max_old_space_size =1900 (64bit os) – --stack_size=4096 – --max_new_space_size=10000 – --use_big_map_space (慎用)
– 语言层面
• 局部变量 • Try {bigset}catch()
– > try {fn} – http://jsperf.com/try-catch-performance-overhead
• TypedArray • Cache • With • 对象转换、copy • String concat …
第13页
开始说说 代码性能+V8
• Benchmark 测试
– 单元测试不仅仅只验证正确性
var suite = new Benchmark.Suite; // add tests suite.add(‘RegExp#test’, function() {
/o/.test(‘Hello World!’); }) .add(‘String#match’, function() {
!!‘Hello World!’.match(/o/); }) // add listeners .on(‘cycle’, function(event, bench) {
…. }) .on(‘complete’, function() {
… }) .run({ ‘async’: true });
第14页
Sample
data length \u0000
data bytes
6 \u0000 T A O b A O
第15页
Step 1
Parser.prototype.parse1 = function (s) { var l = ''; for (var i = 0; i < s.length; i++) { if (s[i] == '\u0000') { l = Number(l); this.emit('data', s.substr(i + 1, l)); return this.parse1(s.substr(i + 1 + l)); } else { l += s[i]; } } return s;
};
第16页
Step 1-Stress
var p = new Parser(); var NOF_RUNS = 1000; var start = Date.now(); for (var j = 0; j < RUN_NUMBERS; j++) {
p.parse3(fakeInput); } var end = Date.now();
var timeSpent = end - start; console.log(timeSpent + ' ms');
400 ms
第17页
Step 1—key profile
[JavaScript]:
ticks total nonlib name
38 15.8% 21.0% Stub: SubStringStub
2 0.8% 1.1% Stub: StringAddStub
2 0.8% 1.1% LazyCompile: *Parser.parse1 /work/project/stress/src/BinFile.js:10
1 0.4% 0.6% Stub: StringAddStub {1}
1 0.4% 0.6% LazyCompile: *substr native string.js:698 [GC]:
ticks total nonlib name
GC成本随长时间存活 对象的个数线性上涨
151 62.9%
-----------------
pause=9 mutator=7 gc=s external=0 mark=0 sweep=0 sweepns=0 compact=0 total_size_before=22049776 total_size_after=22001000 holes_size_before=335256 holes_size_after=335256 allocated=16776520 promoted=7174952
Memory allocator, used: 84180992, available: 1450934272
New space,
used: 9551576, available: 7225640
Old pointers, used: 605384, available: 1710936, waste: 160
Old data space, used: 203208, available: 300712, waste: 16
Code space,
used: 361472, available: 126208, waste: 0
Map space,
used: 39704, available: 207624, waste: 4640
Cell space,
used: 8128, available: 251968, waste: 0
Large object space, used: 13025280, available: 1450926016
第18页
Step 2
Parser.prototype.parse1 = function (s) { var l = ''; for (var i = 0; i < s.length; i++) { if (s[i] == '\u0000') { l = Number(l); this.emit('data', s.substr(i + 1, l)); return this.parse1(s.substr(i + 1 + l)); } else { l += s[i]; } } return s;
};
第19页
Step 2
Parser.prototype.parse1 = function (s) {
var l = '';
for (var i = 0; i < s.length; i++) {
if (s[i] == '\u0000') {
l = Number(l);
this.emit('data', s.substr(i + 1, l));
s = s.substr(i + 1 + l);
i = 0;
l = '';
} else {
l += s[i];
}
}
return s; };
170 ms
第20页
Step2 -profile
[JavaScript]:
ticks total nonlib name
42 18.8% 44.2% Stub: SubStringStub
3 1.3% 3.2% Stub: StringAddStub
2 0.9% 2.1% LazyCompile: *Parser.parse2 /mnt/share/stress/src/BinFile.js:25
1 0.4% 1.1% LazyCompile: b native v8natives.js:1264
[GC]:
ticks total nonlib name
36 16.1%
--------
pause=0 mutator=1 gc=s external=0 mark=0 sweep=0 sweepns=0 compact=0 total_size_before=7550080 total_size_after=3394272 holes_size_before=69824
holes_size_after=69824 allocated=4148080 promoted=0
Memory allocator, used: 71888896, available: 1463226368
New space,
used: 22560, available: 4171744
Old pointers, used: 2060512, available: 245784, waste: 2056
Old data space, used: 252568, available: 259256, waste: 240
Code space,
used: 415616, available: 88320, waste: 0
Map space,
used: 39704, available: 215752, waste: 4640
Cell space,
used: 8128, available: 251968, waste: 0
Large object space, used: 724992, available: 1463218112
第21页
Step 3
Parser.prototype.parse3 = function (s) { var l = ''; //方法3 var j = 0;
for (var i = 0; i < s.length; i++) {
if (s[i] == '\u0000') {
l = Number(l);
this.emit('data', s.substr(i + 1, l));
i += l;
j = i + 1;
} else {
l += s[i]; } }
11 ms
return s.substr(j);
};
第22页
Step3-profile
[JavaScript]:
ticks total nonlib name
1 0.7% 3.4% Stub: CallFunctionStub
[GC]:
ticks total nonlib name
20 13.8%
--------------------------
pause=1 mutator=2 gc=s external=0 mark=0 sweep=0 sweepns=0 compact=0 total_size_before=2880944 total_size_after=2766424 holes_size_before=18528 holes_size_after=30208 allocated=790456 promoted=671920
Memory allocator, used: 70520832, available: 1464594432
New space,
used: 262136, available: 786440
Old pointers, used: 1232664, available: 34592, waste: 712
Old data space, used: 231136, available: 28936, waste: 24
Code space,
used: 434112, available: 53568, waste: 0
Map space,
used: 54208, available: 193120, waste: 4640
Cell space,
used: 8624, available: 243344, waste: 0
Large object space, used: 667648, available: 1464586176
第23页
Step4
Parser.prototype.parse4 = function (s) {
var l = 0, i = 0;
while (i < s.length) {
var ch = s.charCodeAt(i);
if (ch === 0) {
this.emit('data', s.substr(i + 1, l));
i += l + 1;
l = 0;
} else { l = l * 10 + ch;
50X
i ++;
}
}
}; 8 ms
第24页
Step4-profile
[JavaScript]: ticks total nonlib name
[GC]:
ticks total nonlib name
17 12.4%
--------------------------
pause=3 mutator=1 gc=s external=0 mark=0 sweep=0 sweepns=0 compact=0 total_size_before=2889880 total_size_after=2769744 holes_size_before=20472 holes_size_after=25392 allocated=786184 promoted=666304
Memory allocator, used: 70520832, available: 1464594432
New space,
used: 262136, available: 786440
Old pointers, used: 1231248, available: 36048, waste: 672
Old data space, used: 231080, available: 20880, waste: 8
Code space,
used: 438976, available: 64960, waste: 0
Map space,
used: 54152, available: 201304, waste: 4640
Cell space,
used: 8608, available: 243360, waste: 0
Large object space, used: 667648, available: 1464586176
第25页
预编译和v8代码优化 日志
[optimizing: Queue.push / 25d70710ba79 - took 0.064 ms] Compiled: 33 functions with 37333 byte source size in
31.198000ms. [marking NonStringToString 0xc69df07d020 for
recompilation] Bailout in HGraphBuilder: @"NonStringToString": call to a
JavaScript runtime function [disabled optimization for: NonStringToString /
c69df07d021] [marking Buffer.write 0x143784371b80 for recompilation] Bailout in HGraphBuilder: @"Buffer.write": SwitchStatement:
non-literal switch label
.
第26页
Nodejs prof分析方法
1.Linux perf + node deep prof
perf record -R -e cycles -c 10000 -f node ../script.js --ll-prof ll_prof.py --disasm-top=10
2.Node parameter Optimization:
--trace_opt (trace lazy optimization) --trace_opt_stats (trace lazy optimization statistics) --trace_deopt (trace deoptimization) --trace_bailout (print reasons for falling back to using the classic V8 backend) GC: --trace_gc (print one trace line following each garbage collection) --trace_gc_nvp (print one detailed trace line in name=value format after each garbage collection) --print_cumulative_gc_stat (print cumulative GC statistics in name=value format on exit) --trace_gc_verbose (print more details following each garbage collection)
3.Manual
--noprof-auto profiler.startProfiling('startup'); - start/resume collection of data profiler.stopProfiling - pause collection of data
第27页
v8-profiler
• var profiler = require('v8-profiler'); profiler.startProfiling('startup'); slowStartupFoo(); profiler.stopProfiling('startup'); profiler.takeSnapshot('beforeLeak'); leakyFoo(); profiler.takeSnapshot('afterLeak');
第28页
Node App 应用层面运维建议
• 定期收集运行信息(建议秒级别) – process.memoryUsage()
• { rss, heapTotal, heapUsed}
– process.uvCounters()
• eio_init、 req_init、handle_init, • stream_init、tcp_init、udp_init, • prepare_init、check_init • idle_init、async_init • timer_init:、process_init、fs_event_init
• 定期开启profiler
– 收集关键函数调用时间 – 收集堆栈信息
• 其它IO收集
– 请求数、响应时间 – 内部系统交互响应时间等
第29页
分布式设计探讨
第30页
分布式设计 (探讨)
• 单机:
– 多进程 (domain socket)
• cluster • multi-node
• 集群
– 节点无交互
• Proxy(nginx proxy..) • LVS..
– 节点有交互
• RPC (缺点?)
– thrift、rest、web services
• 高并发系统特性
– 消息交互 – 无状态 – 异步?
第31页
Nodejs集群(复杂计算逻辑 +异构系统)
• ZEROMQ
– 跨多种传输协议和方式
• 进程内通讯 • IPC • TCP • 广播
– 多连接模型
• REQ/REP • PUB/SUB • PUSH/PULL
– 全局拓扑
• 智能感知路由
– 无锁 – 异步消息交互 – 低延迟高并发 – 接口高度一致
第32页
REQ/REP模型
第33页
REQ/REP模型
第34页
REQ/REP模型
第35页
REQ/REP模型
第36页
REQ/REP模型
第37页
Sample
var zmq = require('zmq') , sock = zmq.socket('rep');
var i = 0; sock.bindSync(url); sock.on('message', function(msg){
}); 代码不用变
url: ‘ipc:///tmp/zmq‘ --进程间通讯 ‘tcp://*:23456‘ --网络
第38页
推荐:
• 编程规范:
– http://cnodejs.org/blog/?p=4739 – https://github.com/windyrobin/iFrame/
• Blazing fast node.js: 10 performance tips from LinkedIn Mobile
• Efficient JavaScript • JavaScript performance playground
第39页
Q&A