服务的性能主要表现在两个主要方面:
指标包含有QPS、并发数和响应时间
QPS(TPS):每秒钟request/事务 数量
并发数: 系统同时处理的request/事务数
响应时间: 一般取平均响应时间
他们之间的关系:并发数 = QPS * 响应时间
当这些指标达不到我们的系统设计要求,或者说系统预估的访问指标时,就需要通过调优提高指标,或者是增加硬件。
更详细的描述就不在赘述了,因为不是此篇文章的重点。
首先我们先准备一个测试项目,本例的代码稍后展示
接下来我们就可以配置jmeter来对这个服务进行性能测试了。
到这里,一个最基本的测试组就完成了。可以点击启动查看执行效果了,在查看结果树
模块中可以看到发送的请求和收到的相应数据
再来看看聚合报告
聚合报告
中列出了这次测试的报告信息,这些指标的含义:
以上的测试与其说是对node服务的测试,倒不如说是对jmeter的测试,因为我们只发送了一次请求,接下来我们就来一次压测,看看结果会怎么样。
如图修改线程组的设置,在10秒内启动200线程,循环执行10次
从该结果中可以看出,qps为11,中位数大于16秒,接下来我们看看是否有提升的空间。
目前可以检测服务执行瓶颈的方式很多,我使用了应用最为广泛的v8-profiler。
v8-profiler
const profiler = require('v8-profiler');
profiler.startProfiling('1', true);
setTimeout(()=>{
var profile1 = profiler.stopProfiling();
profile1.export()
.pipe(fs.createWriteStream('profile1.cpuprofile'))
.on('finish', function() {
profile1.delete();
});
}, 120000)
这段代码会在项目初始化是启动profiler,然后在120秒后终止,并生成报告。
JavaScript Profiler
项目,查看报告pow
函数,而且还可以查看它的调用栈app.get
->slow
->pow
好了,现在我们回过头来看一下我们的测试项目的全部代码:
const app = require('express')();
const fs = require('fs');
const profiler = require('v8-profiler-node8');
profiler.startProfiling('1', true);
setTimeout(()=>{
var profile1 = profiler.stopProfiling();
profile1.export()
.pipe(fs.createWriteStream('profile1.cpuprofile'))
.on('finish', function() {
profile1.delete();
});
}, 120000)
function slow(factor) {
for (let i = 0; i < Math.pow(10, factor); ++i) {
// blocking the event loop
}
}
app.get('/', (req, res, next) => {
slow(7);
res.json({ success: true });
});
app.listen(3000, () => {
console.log('Listening at http://localhost:3000 ..\n');
});
到了这里,我想不需多说,大家已经非常清楚了。
接下来我们去掉这个slow
再测试一次
因为现在接口没有做任何处理,为了更好地查看效果,我们先对线程组的任务稍作调整
上面说的这个应用只是一个单纯的测试项目,而在实际项目中往往是需要进行登录验证的,可以使用HTTP Cookie 管理器
配置cookie进行接口测试
CSV Data Set Config
可以通过csv提前设置好接口需要使用的一个或多个变量
其它更丰富的元件各位请自行探索吧。
如何检查node内存泄漏的文章很多,这里使用一个非常简单的项目来告诉大家如何使用v8-profiler、JMeter以及Chrome相结合来排查内存漏洞。
//app.js
const app = require("express")();
const fs = require("fs");
const profiler = require("v8-profiler");
let timesRun = 0;
const Leak = require("./leak");
//v8-profiler start
const takeSnapshot = () => {
if (timesRun >= 3) {
clearInterval(interval);
}
var snapshot1 = profiler.takeSnapshot();
snapshot1.export(function(error, result) {
fs.writeFileSync("snapshot" + timesRun + ".heapsnapshot", result);
snapshot1.delete();
});
timesRun++;
return takeSnapshot;
};
const interval = setInterval(takeSnapshot(), 30000);
//v8-profiler end
app.get("/", (req, res, next) => {
Leak.leak();
res.json({ success: true });
});
app.listen(3000, () => {
console.log("Listening at http://localhost:3000 ..\n");
});
//leak.js
let leakArray = [];
function emptyObject() {}
exports.leak = function() {
leakArray.push(new emptyObject());
};
该项目接收一个get请求,然后去执行leak.js文件提供的函数leak
,leak
会向leakArray
中添加1个对象emptyObject
。
我们通过使用v8-profiler
每隔20秒生成一张内存快照。
接下来启动服务,我们仍然使用JMeter向服务加压。
持续向服务加压1分20秒,共发送请求83万多次
此时在项目目录中已经生成了3个heapsnapshot文件,我们现在使用chrome来打开这些文件
对于这个测试项目,直接对比第一张和第三张快照,就可以清楚地看到emptyObject
对象所占用的内存增长最快,其在服务运行40秒后被创建了355175个,而没有一个被销毁。
点击其中一个对象可以看到它的引用关系
因为它一直在被leak
对象引用,所以无法被释放。
全文完