龙空技术网

对node工程进行压力测试与性能分析「干货」

Echa攻城狮 275

前言:

现在我们对“jquerylisten”大概比较关注,咱们都想要剖析一些“jquerylisten”的相关内容。那么小编同时在网上搜集了一些关于“jquerylisten””的相关内容,希望朋友们能喜欢,小伙伴们一起来了解一下吧!

作者:小黎

转发链接:

前言

在系统上线前,为了看下系统能承受多大的并发和并发下的负载情况,常常会先进行了一轮压测。

曾在一个项目的压测过程中,发现服务器的cpu飚的的非常高,而tps,接口耗时、服务可用等都是正常的,这就奇了怪了,自己想了半天也没想出为啥,不得已求助了大佬,大佬说先查看 cpu processor

what?这是啥??虽然听不懂,但可以查嘛╭(╯^╰)╮,可还没等我查出来,大佬直接上手,一顿骚操作,便找出了原因~ 这着实让自己汗颜啊,内功远远不足啊,回来网上找了资料,恶补一把如何分析node工程中的性能问题。

这里给大家分享我学习到的各种工具和方法。

样例项目

为了便于演示,写了个简单的小例子

// app.jsconst crypto = require('crypto')const Koa = require('koa')const Router = require('koa-router');const app = new Koa();const router = new Router();router.get('/crypto', async(ctx, next) => {    const salt = crypto.randomBytes(128).toString('base64')    const hash = crypto.pbkdf2Sync('crypto', salt, 10000, 64, 'sha512').toString('hex')    ctx.body = { hash: hash }    console.log(hash)    ctx.status = 200    next()});let reqNum = 0router.get('/empty', async(ctx, next) => {    ctx.body = { hash: 'empty' }    reqNum++;    ctx.status = 200    next()});app.use(router.routes()).use(router.allowedMethods());app.listen(3000, () => {    console.log("listen 3000")})

基于koa2,有两个路由,一个/crypto,其中的业务逻辑是,使用crypto库对字符串加密;一个是 /empty,没有业务逻辑的接口,就是个空接口

压力测试

压力测试工具市面上有很多种,就不一一列举了,在社区看到有人推荐 autocannon ,就对这个工具做个介绍,官方的简介是 fast HTTP/1.1 benchmarking tool written in Node.js ,使用node编写的压测工具,能比wrk生成更多负载。

install

npm i autocannon -gnpm i autocannon --save

use

提供两种使用方式

命令行 autocannon -c 100 -d 5 -p 2 简单快速api调用 autocannon(opts[, cb]) 便于编写脚本

关键参数有这么几个

-c/--connections NUM 并发连接的数量,默认10-p/--pipelining NUM 每个连接的流水线请求请求数。默认1-d/--duration SEC 执行的时间,单位秒-m/--method METHOD 请求类型 默认GET-b/--body BODY 请求报文体

还有很多参数,大家可以查看官网文档。

这个库目前只能支持一个接口压测,我写了个脚本,可以支持批量压测和生成测试报告,具体代码见文末。

report

下图是对 /empty 接口压测 autocannon -c 100 -d 5 -p 1 结果如下

autocannon-empty

可看到,每秒有100个链接,每个链接一个请求,持续5秒,一共产生 31k 次请求。报告分三部分,第一行表示接口的延迟,第二行表示每秒的请求数(tps),第三行表示每秒返回的字节数。那么,延迟越低,tps越高,就表示接口性能越好,因为empty 是个空接口,所以它的tps=6221还不错,响应时间也很快,我们换成 /crypto 接口在试试

autocannon-crypto

立马看出差距了,这个接口tps只有77,接口耗时达到了1100ms,说明这个接口有很大的优化空间啊

生成性能文件与分析

通过压测工具我们找到了有问题的接口,那接下来,就要对接口进行剖析了,可是光看接口代码,不好分析啊,毕竟没有说服力,我们就需要一份性能报告,用数据说话,下面介绍这个两个方法给大家

V8 Profiler

V8 官方已经为大家考虑到这点了,提供了Profiler工具 使用方式也很快捷,步骤如下(以app.js为例)

生成报告

在启动命令中加上 --prof ,如 node --prof app.js ,在项目根目录会生成isolate-xxxxxxx-v8.log格式的文件,用来记录运行期间的调用栈和时间等信息,其中内容如下(文件较大,就截取最顶端一小截)

v8-version,6,1,534,47,0shared-library,"C:\Program Files\nodejs\node.exe",0x7ff7505f0000,0x7ff751c0f000,0shared-library,"C:\WINDOWS\SYSTEM32\ntdll.dll",0x7ff8718a0000,0x7ff871a61000,0shared-library,"C:\WINDOWS\system32\KERNEL32.DLL",0x7ff870590000,0x7ff87063d000,0shared-library,"C:\WINDOWS\system32\KERNELBASE.dll",0x7ff86e830000,0x7ff86ea18000,0shared-library,"C:\WINDOWS\system32\WS2_32.dll",0x7ff86ee00000,0x7ff86ee6b000,0
分析报告对刚刚生成的log文件分析,还是使用官方提供的工具 node --prof-process isolate-xxxxxxxx-v8.log,生成结果如下(去掉无用的部分)
Statistical profiling result from isolate-00000209B99A60A0-v8.log, (17704 ticks, 8 unaccounted, 0 excluded). [Shared libraries]:   ticks  total  nonlib   name  13795   77.9%          C:\WINDOWS\SYSTEM32\ntdll.dll  ... [JavaScript]:   ticks  total  nonlib   name     12    0.1%   11.3%  Builtin: CallFunction_ReceiverIsAny     ... [C++]:   ticks  total  nonlib   name [Summary]:   ticks  total  nonlib   name     94    0.5%   88.7%  JavaScript      0    0.0%    0.0%  C++      8    0.0%    7.5%  GC  17598   99.4%          Shared libraries      8    0.0%          Unaccounted [C++ entry points]:   ticks    cpp   total   name [Bottom up (heavy) profile]:  Note: percentage shows a share of a particular caller in the total  amount of its parent calls.  Callers occupying less than 1.0% are not shown.   ticks parent  name  13795   77.9%  C:\WINDOWS\SYSTEM32\ntdll.dll   3795   21.4%  C:\Program Files\nodejs\node.exe   3768   99.3%    C:\Program Files\nodejs\node.exe   3287   87.2%      Function: ~pbkdf2 crypto.js:633:16   3287  100.0%        Function: ~exports.pbkdf2Sync crypto.js:628:30   3287  100.0%          Function: ~router.get D:\github\webapp\js\usen\app.js:8:23   3287  100.0%            Function: ~dispatch D:\github\webapp\js\usen\node_modules\_koa-compose@3.2.1@koa-compose\index.js:37:23    ...

报告包含六部分:Shared libraries、JavaScript、C++、Summary、C++ entry points 和 Bottom up (heavy) profile,[JavaScript] 部分列出了 JavaScript 代码执行所占用的 CPU ticks(CPU 时钟周期),[C++] 部分列出了 C++ 代码执行所占用的 CPU ticks,[Summary] 列出了各个部分的占比,[Bottom up] 列出了所有 CPU 占用时间从大到小的函数及堆栈信息。

根据 3287 87.2% Function: ~pbkdf2 crypto.js:633:16 可看出这个函数消耗了 87.2% 的cpu

文件的方式不直观,那我们换个UI界面的,步骤如下先clone v8的仓库下来 git clone 将日志文件转换成 json格式 node --prof-process --preprocess isolate-xxxxxxxxxx-v8.log > v8.json打开 v8/tools/profview/index.html 文件,是个静态界面,在界面中心选择刚生成的 v8.json文件,文件解析成功后,界面如下

v8-ui

具体的功能就不一一解释啦,我们逐层展开,寻找耗时的点,很快便找到耗cpu的地方,如下图

v8-ui-report

node占比是45%,其中 pbkdf2 crypto.js便占用了92%

v8-profiler

除了官方提供之外,我们还可以选择开源大佬的库,v8-profiler ,这个库的创建的时间比较早,6年前便创建了,最近一次更是在一年半前,社区评价还是不错的

生成报告

生成方式很简单,不足的是,需要硬编码在项目中,如下

profiler.startProfiling('', true);setTimeout(function() {  var profile = profiler.stopProfiling('');  profile.export()     .pipe(fs.createWriteStream(`cpuprofile-${Date.now()}.cpuprofile`))     .on('finish', () => profile.delete())}, 1000);
解析报告Chrome

我们的大Chrome要出马啦,在Chrome的控制台,有一栏 JavaScript Profile 如下图

chrom-cpu

点击load,选择刚刚生成的文件,解析后如下

chrom-report

逐层查看,便了然

flamegraph-火焰图

使用 flamegraph 生成酷炫的火焰图,用在报告那是酷炫的一逼,官网图如下

flameGraph

使用方式就不细说啦

v8-analytics

这个是社区大佬们,写的一个开源库 v8-analytics,官方介绍如下

解析v8-profiler和heapdump等工具输出的cpu & heap-memory日志,可以提供

v8引擎逆优化或者优化失败的函数标红展示以及优化失败原因展示函数执行时长超过预期标红展示当前项目中可疑的内存泄漏点展示

对应的命令如下

va test bailout --only 这个命令可以只把那些v8引擎逆优化的函数列出来展示。

va test timeout 200 --only 这个命令可以只把那些指时长超过200ms的函数列出来展示。

va test leak 可以展示出测试的heapsnapshot文件中可疑的内存泄漏点。

这个库的好处是,省的我们一个个去点开查找,这样可以更加便于我们筛选问题啦~

批量压力测试及生成报告

autocannon 只能运行一个接口,要想在测试下一个接口,就得修改代码,比如想批量测试多个接口,就需要来回改代码,操作就比较麻烦,所以我基于 autocannon 写了个脚本,可以逐一压测定义好的接口,同时还可以生成测试报告。

// app.jsconst crypto = require('crypto')const Koa = require('koa')const Router = require('koa-router');const app = new Koa();const router = new Router();router.get('/crypto', async(ctx, next) => {    const salt = crypto.randomBytes(128).toString('base64')    const hash = crypto.pbkdf2Sync('crypto', salt, 10000, 64, 'sha512').toString('hex')    ctx.body = { hash: hash }    console.log(hash)    ctx.status = 200    next()});let reqNum = 0router.get('/empty', async(ctx, next) => {    ctx.body = { hash: 'empty' }    reqNum++;    ctx.status = 200    next()});app.use(router.routes()).use(router.allowedMethods());app.listen(3000, () => {    console.log("listen 3000")})
小结

在开发过程中,因为过于只关注了业务逻辑的实现,一些可能出现性能的点被忽略掉,而且这些点只能在量稍微大些的并发场景下才会出现,忘了在哪看到一句话 可能会出问题的点,便一定会出问题 性能问题进行分析必不可少。

以上的方法基本上能满足我们的需求,当然性能涉及的方方面面很多比如内存泄漏、事物等,性能调优路漫漫呀, 文章大部分东西都是来自大佬们的总结,我只是在做一次整理汇总,便于自己理解与查阅,希望能帮到小伙伴们~

推荐JavaScript学习相关文章

《前端实现最佳截图方案(上)》

《前端实现最佳截图方案(下)》

《Node.js 实现抢票小工具&短信通知提醒(上)「干货」》

《Node.js 实现抢票小工具&短信通知提醒(下)「干货」》

《那些不常见,但却非常实用的JS知识点(上)》

《那些不常见,但却非常实用的JS知识点(下)》

《如何为团队定制自己的 Node.js 框架?》

《细品原生JS从初级到高级知识点汇总(一)》

《细品原生JS从初级到高级知识点汇总(二)》

《细品原生JS从初级到高级知识点汇总(三)》

《细品原生JS从初级到高级知识点汇总(四)》

《学习 jQuery 源码整体架构,打造属于自己的 js 类库》

《细说RTSP实现前端直播流探索记「干货」》

《一文带你搞懂前端登陆设计》

《使用 Node.js 将图片中的苹果变成橘子「实践」》

《基于Canvas实现的高斯模糊(上)「JS篇」》

《基于Canvas实现的高斯模糊(下)「JS篇」》

《由浅入深,66条JavaScript面试知识点(一)》

《由浅入深,66条JavaScript面试知识点(二)》

《由浅入深,66条JavaScript面试知识点(三)》

《由浅入深,66条JavaScript面试知识点(四)》

《由浅入深,66条JavaScript面试知识点(五)》

《由浅入深,66条JavaScript面试知识点(六)》

《由浅入深,66条JavaScript面试知识点(七)》

《为什么 setTimeout 有最小时延 4ms ?》

《如何处理 Node.js 中出现的未捕获异常?》

《Angular v10.0.0 正式发布,不再支持 IE9/10》

《基于 Docker 的 SSR 持续开发集成环境实践》

《细聊图解webpack 指南手册》

《一文带你彻底搞懂 NPM 知识点「进阶篇」》

《细聊webpack性能优化面面观》

《JS实现各种日期操作方法汇总》

《「实践」细聊前端性能优化总结》

《「实践」浏览器中的画中画(Picture-in-Picture)模式及其 API》

《「多图」一文带你彻底搞懂 Web Workers (上)》

《「多图」一文带你彻底搞懂 Web Workers (中)》

《深入细聊前端下载总结「干货」》

《细品西瓜播放器功能分析(上)「实践」》

《细品西瓜播放器功能分析(下)「实践」》

《细聊50道JavaScript基础面试题「附答案」》

《webpack4主流程源码解说以及动手实现一个简单的webpack(上)》

《webpack4主流程源码解说以及动手实现一个简单的webpack(下)》

《细聊前端架构师的视野》

《细聊应用场景再谈防抖和节流「进阶篇」》

《前端埋点统一接入方案实践》

《细聊微内核架构在前端的应用「干货」》

《一种高性能的Tree组件实现方案「干货」》

《进击的JAMStack》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中》

《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下》

《一文带你搞懂 babel-plugin-import 插件(上)「源码解析」》

《一文带你搞懂 babel-plugin-import 插件(下)「源码解析」》

《JavaScript常用API合集汇总「值得收藏」》

《推荐10个常用的图片处理小帮手(上)「值得收藏」》

《推荐10个常用的图片处理小帮手(下)「值得收藏」》

《JavaScript 中ES6代理的实际用例》

《12 个实用的前端开发技巧总结》

《一文带你搞懂搭建企业级的 npm 私有仓库》

《教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?》

《细说前端开发UI公共组件的新认识「实践」》

《细说DOM API中append和appendChild的三个不同点》

《细品淘系大佬讲前端新人如何上王者「干货」》

《一文带你彻底解决背景跟随弹窗滚动问题「干货」》

《推荐常用的5款代码比较工具「值得收藏」》

《Node.js实现将文字与图片合成技巧》

《爱奇艺云剪辑Web端的技术实现》

《我再也不敢说我会写前端 Button组件「实践」》

《NodeX Component - 滴滴集团 Node.js 生态组件体系「实践」》

《Node Buffers 完整指南》

《推荐18个webpack精美插件「干货」》

《前端开发需要了解常用7种JavaScript设计模式》

《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》

《了不起的 Webpack HMR 学习指南(上)「含源码讲解」》

《了不起的 Webpack HMR 学习指南(下)「含源码讲解」》

《10个打开了我新世界大门的 WebAPI(上)「实践」》

《10个打开了我新世界大门的 WebAPI(中)「实践」》

《10个打开了我新世界大门的 WebAPI(下)「实践」》

《「图文」ESLint 在中大型团队的应用实践》

《Deno是代码的浏览器,你认同吗?》

《前端存储除了 localStorage 还有啥?》

《Javascript 多线程编程​的前世今生》

《微前端方案 qiankun(实践及总结)》

《「图文」V8 垃圾回收原来这么简单?》

《Webpack 5模块联邦引发微前端的革命?》

《基于 Web 端的人脸识别身份验证「实践」》

《「前端进阶」高性能渲染十万条数据(时间分片)》

《「前端进阶」高性能渲染十万条数据(虚拟列表)》

《图解 Promise 实现原理(一):基础实现》

《图解 Promise 实现原理(二):Promise 链式调用》

《图解 Promise 实现原理(三):Promise 原型方法实现》

《图解 Promise 实现原理(四):Promise 静态方法实现》

《实践教你从零构建前端 Lint 工作流「干货」》

《高性能多级多选级联组件开发「JS篇」》

《深入浅出讲解Node.js CLI 工具最佳实战》

《延迟加载图像以提高Web网站性能的五种方法「实践」》

《比较 JavaScript 对象的四种方式「实践」》

《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》

《使用Service Worker让你的 Web 应用如虎添翼(下)「干货」》

《前端如何一次性处理10万条数据「进阶篇」》

《推荐三款正则可视化工具「JS篇」》

《如何让用户选择是否离开当前页面?「JS篇」》

《JavaScript开发人员更喜欢Deno的五大原因》

《仅用18行JavaScript实现一个倒数计时器》

《图文细说JavaScript 的运行机制》

《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》

《推荐Web程序员常用的15个源代码编辑器》

《10个实用的JS技巧「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》

《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》

《深入JavaScript教你内存泄漏如何防范》

《手把手教你7个有趣的JavaScript 项目-上「附源码」》

《手把手教你7个有趣的JavaScript 项目-下「附源码」》

《JavaScript 使用 mediaDevices API 访问摄像头自拍》

《手把手教你前端代码如何做错误上报「JS篇」》

《一文让你彻底搞懂移动前端和Web 前端区别在哪里》

《63个JavaScript 正则大礼包「值得收藏」》

《提高你的 JavaScript 技能10 个问答题》

《JavaScript图表库的5个首选》

《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》

《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》

《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》

《可视化的 js:动态图演示 Promises & Async/Await 的过程》

《原生JS封装拖动验证滑块你会吗?「实践」》

《如何实现高性能的在线 PDF 预览》

《细说使用字体库加密数据-仿58同城》

《Node.js要完了吗?》

《Pug 3.0.0正式发布,不再支持 Node.js 6/8》

《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》

《JavaScript 20 年 中文版之创立标准》

《值得收藏的前端常用60余种工具方法「JS篇」》

《箭头函数和常规函数之间的 5 个区别》

《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》

《「前端篇」不再为正则烦恼》

《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》

《深入细品浏览器原理「流程图」》

《JavaScript 已进入第三个时代,未来将何去何从?》

《前端上传前预览文件 image、text、json、video、audio「实践」》

《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》

《推荐13个有用的JavaScript数组技巧「值得收藏」》

《前端必备基础知识:window.location 详解》

《不要再依赖CommonJS了》

《犀牛书作者:最该忘记的JavaScript特性》

《36个工作中常用的JavaScript函数片段「值得收藏」》

《Node + H5 实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的 JavaScript 模块化方案和工具》

《「前端进阶」JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript 面试题》

《一行JS代码实现一个简单的模板字符串替换「实践」》

《JS代码是如何被压缩的「前端高级进阶」》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个 JavaScript 版敏感词过滤库》

《送你 43 道 JavaScript 面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂 JavaScript 执行机制》

《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript 工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js 动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解 js 深拷贝 vs 浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS 异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS 经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的 JS 写得更漂亮》

《恭喜你获得治疗JS this的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于 JavaScript 作用域的 5 个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript+Canvas实现自定义画板》

《Continuation 在 JS 中的应用「前端篇」》

作者:小黎

转发链接:

标签: #jquerylisten