前言 在做性能优化的时候关注的重点一般是web端的性能,但在有Node作为中间层使用的情况下Node端的性能同样需要去关注。
比如遇到网络请求慢的情况,原因除了网速问题以外可能也是Node端响应时间较长,所以就需要去抓取出性能耗费点在哪里。
性能指标 服务端的性能指标比较多,所以我们只需要关注RT和QPS这两个影响页面性能和服务器消耗的就行了
RT:响应时长,系统对请求作出的响应时间(单次请求耗时)
QPS:单台机器每秒处理查询数量 QPS的统计方式:1 QPS = 总请求数 / ( 进程总数 * 请求时间 )
假如一个请求的RT从500ms降低到100ms,那么理论上QPS就可以提升4倍,以前需要五台机器才能扛住的流量现在只需要一台,这样可以在减少响应时间的时候同时减少服务器资源消耗
抓取Node端性能 那应该怎样去抓取Node端请求的RT呢,Node本身是自带分析profile的,但是它导出的是日志分析,不太好判断性能消耗点在什么地方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Concurrency Level: 20 Time taken for tests: 46.932 seconds Complete requests: 250 Failed requests: 0 Keep-Alive requests: 250 Total transferred: 50250 bytes HTML transferred: 500 bytes Requests per second: 5.33 [#/sec] (mean) Time per request: 3754.556 [ms] (mean) Time per request: 187.728 [ms] (mean, across all concurrent requests) Transfer rate: 1.05 [Kbytes/sec] received ... Percentage of the requests served within a certain time (ms) 50% 3755 66% 3804 75% 3818 80% 3825 90% 3845 95% 3858 98% 3874 99% 3875 100% 4225 (longest request)
所以我们可以用v8-profiler-next ,这个插件可以对CPU和堆内存进行抓取,因为我们想要的是请求耗时的数据所以只抓CPU就行了。
在开发/测试环境下还需要使用loadtest 压力测试工具做一次CPU密集计算获取耗时平均值。
以电影站请求为例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 router.get('/api/home/movie-list' , controller.home.getMovieList) router.get('/api/cpuprofile' ,controller.home.exportCPUProfile) const profiler = require ('v8-profiler-next' )const fs = require ('fs' )... async getMovieList(ctx) { return ctx.helper.commonStr({ ctx, type : “get ”, serverName: “home”, fnName: “getHomeList” }) } // service/home async getHomeList(opt: { domain: string, isAmp : string }) { let { domain, isAmp } = opt let url = this .ctx.helper.parseParam({ auth: this .app.config.apiAuth, domain }, API.MOVIE_HOME_LIST_URL) try { let data: any = await this .ctx.service.mc.getMC(url) ... } catch (err) { .. } } async exportCPUProfile() { profiler.startProfiling('CPU profile' ) setTimeout(() => { const profile = profiler.stopProfiling() profile.export() .pipe(fs.createWriteStream(`cpuprofile-${Date .now()} .cpuprofile` )) .on('finish' , () => profile.delete()) console .log('finish' ) }, 60000 ) this .ctx.body = { status: 'success' } } ...
然后开两个终端分别运行
1 curl http://127.0.0.1:8083/api/cpuprofile
1 loadtest http://127.0.0.1:8083/api/home/movie-list/?domain=movierulz.video&&isAmp=false -n 100
结束抓取后导出的.cpuprofile
文件我们是不能直接去看的,还要通过Chrome devtools 提供的CPU分析页面展示抓取到的数据。 进入JavaScript Profiler 页面后点击Load 按钮导入上面的profile文件就能得到分析结果。 分析结果有两个指标:
Self Time : 函数调用所耗费的时间,仅包含函数本身的声明,不包含任何子函数的执行时间。
Total Time : 函数调用所耗费的总时间,包含函数本身的声明及所有子函数执行时间
所以只需要看Total Time 即可,然后根据Total Time 去定位问题作出具体的优化措施。
参考资料 1.Node.js 应用故障排查手册 —— 正确打开 Chrome devtools 2.使用 v8-profiler 分析 CPU 的使用情况 3.React同构与极致的性能优化 4.QPS、PV 、RT(响应时间)之间的关系