性能测试工具 性能测试工具 nGrinder 项目剖析及二次开发

neven7 · 2016年02月26日 · 最后由 yeshunkai 回复于 2018年09月18日 · 2621 次阅读
本帖已被设为精华帖!

0.背景

组内需要一款轻量级的性能测试工具,之前考虑过LR(太笨重,单实例,当然它的地位是不容置疑的),阿里云的PTS(https://pts.aliyun.com/lite/index.htm, 仅支持阿里云内网和公网机器),Gatling(http://gatling.io/#/)没有TPS数据等等,不太适合我们。

nGrinderr是NAVER(韩国最大互联网公司NHN旗下搜索引擎网站)开源的性能测试工具,直接部署成web服务,支持多用户使用,可扩展性好,可自定义plugin(http://www.cubrid.org/wiki_ngrinder/entry/how-to-develop-plugin),wiki文档较丰富(http://www.cubrid.org/wiki_ngrinder/entry/ngrinder-devzone),数据及图形化展示满足需求;但是展示的统计数据较简单,二次开发整合数据:TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数字段,并将这些数据展示在详细测试报告页中。

1.项目剖析

1-1. nGrinder架构

nGrinder是一款在一系列机器上执行Groovy或JPython测试脚本的应用,内部引擎是基于Grinder。
架构图:
这里写图片描述

层级图:
这里写图片描述

默认的NGRINDER_HOME为/root/.ngrinder, 大多是配置文件和数据文件。

这里写图片描述

目录/root/.ngrinder/perftest/0_999下,以每个test_id为名的文件夹对应的存储了执行性能测试时的采样数据:
这里写图片描述

*.data文件就是执行性能测试时对应的各种性能采样数据,性能测试详细报告页就是根据这些data文件,进行图形化展示(ajax)。

nGrinder包含2大组件:
1)Controller
为性能测试提供web interface
协同测试进程
收集和显示测试数据
新建和修改脚本

2)Agent
agent mode: 运行进程和线程,压测目标服务
monitor mode: 监控目标系统性能(cpu/memory), 可以自定义收集的数据(比如 jvm数据)

http://www.cubrid.org/wiki_ngrinder/entry/general-architecture

1-2. 技术栈

1)Controller 层
FreeMarker: 基于Java的模板引擎
Spring Security
Spring Mvc:Spring MVC provides rich functionality for building robust web applications.
GSon
SVNKit Dav

2)Service 层
Grinder
Spring
EhCache: Ehcache has excellent Spring integration.

3)Data层
Spring Data
Hibernate:Hibernate is a powerful technology for persisting data,and it is Spring Data back-end within nGrinder.
H2: (nGrinder默认使用该DB)
Cubrid:(nGrinder同一家公司的DB)
Liquidase: Liquibase is an open source that automates database schema updates.
SVNKit

http://www.cubrid.org/wiki_ngrinder/entry/technology-stack

2.源码实现

需求:在详细测试报告页中展示TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数这些数据。

修改Controller层,增加数据处理业务逻辑(计算TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数)

在获取采样数据
ngrinder-core/src/main/java/net/grinder/SingleConsole.java中新增处理业务逻辑,核心修改代码片段:

    // tps list
List<Double> tps = new CopyOnWriteArrayList<Double>();
// rt list
List<Double> meanTestTime = new CopyOnWriteArrayList<Double>();

/**
*
* 每次请求调用一次 Build up statistics for current sampling.
*
* @param accumulatedStatistics
* intervalStatistics
* @param intervalStatistics
* accumulatedStatistics
*/

protected void updateStatistics(StatisticsSet intervalStatistics,
StatisticsSet accumulatedStatistics) {
Map<String, Object> result = newHashMap();
result.put("testTime", getCurrentRunningTime() / 1000);
List<Map<String, Object>> cumulativeStatistics = new ArrayList<Map<String, Object>>();
List<Map<String, Object>> lastSampleStatistics = new ArrayList<Map<String, Object>>();

for (Test test : accumulatedStatisticMapPerTest.keySet()) {
Map<String, Object> accumulatedStatisticMap = newHashMap();
Map<String, Object> intervalStatisticsMap = newHashMap();
StatisticsSet accumulatedSet = this.accumulatedStatisticMapPerTest
.get(test);
StatisticsSet intervalSet = this.intervalStatisticMapPerTest
.get(test);

accumulatedStatisticMap.put("testNumber", test.getNumber());
accumulatedStatisticMap.put("testDescription",
test.getDescription());
intervalStatisticsMap.put("testNumber", test.getNumber());
intervalStatisticsMap.put("testDescription", test.getDescription());
// When only 1 test is running, it's better to use the parametrized
// snapshot.
for (Entry<String, StatisticExpression> each : getExpressionEntrySet()) {
if (INTERESTING_STATISTICS.contains(each.getKey())) {
accumulatedStatisticMap.put(
each.getKey(),
getRealDoubleValue(each.getValue().getDoubleValue(
accumulatedSet)));
intervalStatisticsMap.put(
each.getKey(),
getRealDoubleValue(each.getValue().getDoubleValue(
intervalSet)));
}
}
cumulativeStatistics.add(accumulatedStatisticMap);
lastSampleStatistics.add(intervalStatisticsMap);
}

Map<String, Object> totalStatistics = newHashMap();

for (Entry<String, StatisticExpression> each : getExpressionEntrySet()) {
if (INTERESTING_STATISTICS.contains(each.getKey())) {
totalStatistics.put(each.getKey(), getRealDoubleValue(each
.getValue().getDoubleValue(accumulatedStatistics)));
}
}

LOGGER.debug("hugang start get plug data");

// 获取tps, rt集合
for (Entry<String, StatisticExpression> each : getExpressionEntrySet()) {
if ("TPS".equals(each.getKey())) {
tps.add((Double) getRealDoubleValue(each.getValue()
.getDoubleValue(intervalStatistics)));
} else if ("Mean_Test_Time_(ms)".equals(each.getKey())) {
meanTestTime.add((Double) getRealDoubleValue(each.getValue()
.getDoubleValue(intervalStatistics)));
}
}


result.put("totalStatistics", totalStatistics);
result.put("cumulativeStatistics", cumulativeStatistics);
result.put("lastSampleStatistics", lastSampleStatistics);
result.put("tpsChartData", getTpsValues());
result.put("peakTpsForGraph", this.peakTpsForGraph);
synchronized (this) {
result.put(GrinderConstants.P_PROCESS, this.runningProcess);
result.put(GrinderConstants.P_THREAD, this.runningThread);
result.put("success", !isAllTestFinished());
}
// Finally overwrite.. current one.
this.statisticData = result;
}

/**
* 从updateStatistics()累加数据, list :rt 和 tps, 为成员变量
*
* 再处理集合,放到statisticData中
*
* @author hugang
*/

public void getPlusResult(){

LOGGER.debug("hugang getPlusResult() tpslist {} rtlist is {}",
tps.toString(), meanTestTime.toString());

int i = 0;
int j = 0;
// list转成数组, 标准库使用数组作为参数
double[] tpsArray = new double[tps.size()];
for (double tpsNum : tps) {
tpsArray[i++] = tpsNum;
}

// list转成数组
double[] meanTestTimeArray = new double[meanTestTime.size()];
for (double meanTime : meanTestTime) {
meanTestTimeArray[j++] = meanTime;
}

// tps 标准差
double tpsStd = new StandardDeviation().evaluate(tpsArray);
// tps 平均值
double tpsMean = new Mean().evaluate(tpsArray, 0, tpsArray.length);
// tps 波动率= tps 标准差 / tps 平均值
double tpsVix = 0;
if(0 != tpsMean){
tpsVix = tpsStd / tpsMean;
}

// meanTestTime 百分位数
Percentile percentile = new Percentile();
// 先排序
Arrays.sort(meanTestTimeArray);
// meanTestTime最小值
double minMeanTime = meanTestTimeArray[0];
double twentyFiveMeanTime = percentile.evaluate(meanTestTimeArray, 25);
double fiftyMeanTime = percentile.evaluate(meanTestTimeArray, 50);
double serventyFiveMeanTime = percentile
.evaluate(meanTestTimeArray, 75);
double eightyMeanTime = percentile.evaluate(meanTestTimeArray, 80);
double eightyFiveMeanTime = percentile.evaluate(meanTestTimeArray, 85);
double ninetyMeanTime = percentile.evaluate(meanTestTimeArray, 90);
double ninetyFiveMeanTime = percentile.evaluate(meanTestTimeArray, 95);
double ninetyNineMeanTime = percentile.evaluate(meanTestTimeArray, 99);

int length = meanTestTimeArray.length;
// meanTestTime最高值
double maxMeanTime = meanTestTimeArray[length - 1];
// meanTestTime平均值
// double TimeMean = new Mean().evaluate(meanTestTimeArray, 0,
// meanTestTimeArray.length);

LOGGER.debug(
"hugang plug Statistics MinMeanTime {} MaxMeanTime is {}",
minMeanTime, maxMeanTime);
// 附加信息 hugang
// tps 标准差, tps 波动率, 最小/最大RT, RT百分位数
Map<String, Object> plusStatistics = newHashMap();
plusStatistics.put("tpsStd", tpsStd);
// plusStatistics.put("tpsMean", tpsMean);
plusStatistics.put("tpsVix", tpsVix);
plusStatistics.put("minMeanTime", minMeanTime);
plusStatistics.put("twentyFiveMeanTime", twentyFiveMeanTime);
plusStatistics.put("fiftyMeanTime", fiftyMeanTime);
plusStatistics.put("serventyFiveMeanTime", serventyFiveMeanTime);
plusStatistics.put("eightyMeanTime", eightyMeanTime);
plusStatistics.put("eightyFiveMeanTime", eightyFiveMeanTime);
plusStatistics.put("ninetyMeanTime", ninetyMeanTime);
plusStatistics.put("ninetyFiveMeanTime", ninetyFiveMeanTime);
plusStatistics.put("ninetyNineMeanTime", ninetyNineMeanTime);
plusStatistics.put("maxMeanTime", maxMeanTime);


LOGGER.debug("SingleConsole plug Statistics map plusStatistics {}", plusStatistics);


this.statisticData.put("plusStatistics", plusStatistics);
}



/**
*
* 停止采样数据
* Stop sampling.
*/

public void unregisterSampling() {
this.currentNotFinishedProcessCount = 0;
if (sampleModel != null) {
this.sampleModel.reset();
this.sampleModel.stop();
}
LOGGER.info("Sampling is stopped");
informTestSamplingEnd();

// 结束采样后,处理数据
// hugang
getPlusResult();
}



Map statisticData为不同数据集集合。

Service层从SingleConsole类中获取数据集statisticData:
ngrinder-controller/src/main/java/org/ngrinder/perftest/server/PerfTestService.java 中Map<String, Object> result = consoleManager.getConsoleUsingPort(perfTest.getPort()).getStatisticsData();


/**
* Update the given {@link PerfTest} properties after test finished.
*
* @param perfTest perfTest
*
* getConsoleUsingPort()获取数据
*
*
* hugang
*/

public void updatePerfTestAfterTestFinish(PerfTest perfTest) {
checkNotNull(perfTest);
Map<String, Object> result = consoleManager.getConsoleUsingPort(perfTest.getPort()).getStatisticsData();
@SuppressWarnings("unchecked")
Map<String, Object> totalStatistics = MapUtils.getMap(result, "totalStatistics", MapUtils.EMPTY_MAP);
// 获取附加数据
Map<String, Object> plusStatistics = MapUtils.getMap(result, "plusStatistics", MapUtils.EMPTY_MAP);

LOGGER.info("Total Statistics for test {} is {}", perfTest.getId(), totalStatistics);
LOGGER.info("plug Statistics for test {} is {}", perfTest.getId(), plusStatistics);

perfTest.setTps(parseDoubleWithSafety(totalStatistics, "TPS", 0D));
perfTest.setMeanTestTime(parseDoubleWithSafety(totalStatistics, "Mean_Test_Time_(ms)", 0D));
perfTest.setPeakTps(parseDoubleWithSafety(totalStatistics, "Peak_TPS", 0D));
perfTest.setTests(MapUtils.getDouble(totalStatistics, "Tests", 0D).longValue());
perfTest.setErrors(MapUtils.getDouble(totalStatistics, "Errors", 0D).longValue());


// 附加信息写到model, 持久化
perfTest.setTpsStd(parseDoubleWithSafety(plusStatistics, "tpsStd", 0D));
perfTest.setTpsVix(parseDoubleWithSafety(plusStatistics, "tpsVix", 0D));
perfTest.setMinRT(parseDoubleWithSafety(plusStatistics, "minMeanTime", 0D));
perfTest.setTwentyFiveMeanTime(parseDoubleWithSafety(plusStatistics, "twentyFiveMeanTime", 0D));
perfTest.setFiftyMeanTime(parseDoubleWithSafety(plusStatistics, "fiftyMeanTime", 0D));
perfTest.setServentyFiveMeanTime(parseDoubleWithSafety(plusStatistics, "serventyFiveMeanTime", 0D));
perfTest.setEightyMeanTime(parseDoubleWithSafety(plusStatistics, "eightyMeanTime", 0D));
perfTest.setEightyFiveMeanTime(parseDoubleWithSafety(plusStatistics, "eightyFiveMeanTime", 0D));
perfTest.setNinetyMeanTime(parseDoubleWithSafety(plusStatistics, "ninetyMeanTime", 0D));
perfTest.setNinetyFiveMeanTime(parseDoubleWithSafety(plusStatistics, "ninetyFiveMeanTime", 0D));
perfTest.setNinetyNineMeanTime(parseDoubleWithSafety(plusStatistics, "ninetyNineMeanTime", 0D));
perfTest.setMaxRT(parseDoubleWithSafety(plusStatistics, "maxMeanTime", 0D));


}

修改Model层,在javabean中增加TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数, JPA持久化(H2 DB新增TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数字段)

model文件为:ngrinder-core/src/main/java/org/ngrinder/model/PerfTest.java


/**
* 新增字段,TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数
* hugang
*/

@Expose
@Column(name = "tpsStd")
private Double tpsStd;

@Expose
@Column(name = "tpsVix")
private Double tpsVix;


@Expose
@Column(name = "minRT")
private Double minRT;

@Expose
@Column(name = "twentyFiveMeanTime")
private Double twentyFiveMeanTime;

@Expose
@Column(name = "fiftyMeanTime")
private Double fiftyMeanTime;

@Expose
@Column(name = "serventyFiveMeanTime")
private Double serventyFiveMeanTime;

@Expose
@Column(name = "eightyMeanTime")
private Double eightyMeanTime;

@Expose
@Column(name = "eightyFiveMeanTime")
private Double eightyFiveMeanTime;

@Expose
@Column(name = "ninetyMeanTime")
private Double ninetyMeanTime;

@Expose
@Column(name = "ninetyFiveMeanTime")
private Double ninetyFiveMeanTime;

@Expose
@Column(name = "ninetyNineMeanTime")
private Double ninetyNineMeanTime;

@Expose
@Column(name = "maxRT")
private Double maxRT;


对应的set(), get()


还需修改db change文件(因为系统DB默认使用H2, 只需修改H2对应的xml),ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog_schema_H2.xml


create table PERF_TEST (
id bigint generated by default as identity unique,
created_date timestamp,
last_modified_date timestamp,
agent_count integer,
description varchar(2048),
distribution_path varchar(255),
duration bigint,
errors integer,
finish_time timestamp,
ignore_sample_count integer,
init_processes integer,
init_sleep_time integer,
last_progress_message varchar(2048),
mean_test_time double,
peak_tps double,
errorRate double,
tpsStd double,
tpsVix double,
minRT double,
twentyFiveMeanTime double,
fiftyMeanTime double,
serventyFiveMeanTime double,
eightyMeanTime double,
eightyFiveMeanTime double,
ninetyMeanTime double,
ninetyFiveMeanTime double,
ninetyNineMeanTime double,
maxRT double,

系统重启加载时,Liquidase会自动更新DB。

修改View层,在详细报告对应的freemarker模板新增TPS标准差,TPS波动率,最小/大RT,RT 25/50/75/80/85/90/95/99百分位数字段,前端新增展示这些数据

ngrinder-controller/src/main/webapp/WEB-INF/ftl/perftest/detail_report.ftl



<#-- hugang -->
<#-- 新增 错误率,TPS标准差,TPS波动率,最小RT, 最大RT, RT 25/50/75/80/85/90/95/99百分位数 -->
<tr>
<th><@spring.message "perfTest.report.errorRate"/></th>
<td>${(test.errors /(test.tests + test.errors))!""}</td>
</tr>
<tr>
<th><@spring.message "perfTest.report.tpsStd"/></th>
<td>${test.tpsStd!""}</td>
</tr>
<tr>
<th><@spring.message "perfTest.report.tpsVix"/></th>
<td>${test.tpsVix!""}</td>
</tr>
<tr>
<th><@spring.message "perfTest.report.minRT"/></th>
<td>${test.minRT!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
<tr>
<th><@spring.message "perfTest.report.TwentyFiveMeanTime"/></th>
<td>${test.twentyFiveMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
<tr>
<th><@spring.message "perfTest.report.FiftyMeanTime"/></th>
<td>${test.fiftyMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
<tr>
<th><@spring.message "perfTest.report.ServentyFiveMeanTime"/></th>
<td>${test.serventyFiveMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
<tr>
<th><@spring.message "perfTest.report.EightyMeanTime"/></th>
<td>${test.eightyMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
</tr>
<tr>
<th><@spring.message "perfTest.report.EightyFiveMeanTime"/></th>
<td>${test.eightyFiveMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
</tr>
<tr>
<th><@spring.message "perfTest.report.NinetyMeanTime"/></th>
<td>${test.ninetyMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
</tr>
<tr>
<th><@spring.message "perfTest.report.NinetyFiveMeanTime"/></th>
<td>${test.ninetyFiveMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
</tr>
<tr>
<th><@spring.message "perfTest.report.NinetyNineMeanTime"/></th>
<td>${test.ninetyNineMeanTime!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>
</tr>
<tr>
<th><@spring.message "perfTest.report.maxRT"/></th>
<td>${test.maxRT!""}&nbsp;&nbsp; <code>ms</code></td>
</tr>

还有个坑,就是从github拉下的代码,源码中pom.xml依赖的jar包不完整,直接打不了包,项目有的依赖的jar 公有maven仓库已经没有了,需要自己从网上找jar包,安装到本地仓库,我归整了下:

http://download.csdn.net/detail/neven7/9443895

直接在ngrinder根路径下执行打包命令:

mvn -Dmaven.test.skip=true clean package

部署生成的war即可。

3.结果展示

在详细报告页新增如下数据结果:
这里写图片描述

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 40 条回复 时间 点赞

性能工具又多了一项实践

很好的实践! 以后值得借鉴!

很好的实践!

刚开始接触这个平台不久,楼主是否可以多多分析源码。

很赞!阿里的pts性能测试,不知道是不是也是用nGrinder框架做的?

谢谢分享!

这个能否用来测试android手机的性能指标呢??

#4楼 @shijin880921 还是以实际应用为主,用到哪块具体分析

#5楼 @cpfeng0124 应该不是,不过设计思路都差不多。

neven7 #10 · 2016年02月28日 作者

#7楼 @rockyrock 不适合,定位不一样

#8楼 @neven7 现在我想监控到 cup 的load 值,但是不懂怎么搞。

我们公司用的性能测试工具也是基于Grinder做的二次开发。模拟并发和读取用户数据,用到了shell和python脚本;然后收集施压机和被压机的性能指标,通过Excel的VBA绘制出cpu、内存、硬盘等报告。针对nGrinder有两个问题想请教下:
1.前面说nGrinder的详细报告是通过采集*.data得来的,那么性能报告页也是默认生成的吗?
2.需要对页面上的多个模块进行性能监控,报告的图表中能反映出来吗?

neven7 #14 · 2016年03月09日 作者

#13楼 @rikufly
第一个问题:nGrinder是用来SpringMVC架构,View层是用了freemarker模板展示前端,没太理解你说的默认生成是什么意思?它不是静态页面,是动态生成的;性能报告页性能报告页中有2部分数据,第一部分是从DB中获取的,另一部分是从*data文件展示时序图;详见:https://testerhome.com/topics/4341
第二个问题:前端压测需要录制生成脚本,请参见:http://www.cubrid.org/wiki_ngrinder/entry/recorder;性能数据都能反映。

谢谢分享。

问个问题,ngrinder中如果用maven工程,好像每次执行脚本都会去下载对应的jar包到lib文件夹内,能否控制,第一次下载就好了,如果没更改就不需要再下载?

弱弱地问一句,为什么不用jmeter呢?

neven7 #18 · 2016年06月05日 作者

#17楼 @taurus 这种问题,就好比选择哪种编程语言;不选择jmeter的原因是它是单实例,我们想把测试脚本和结果大家都能直接共享,包括开发也能直接查看性能测试结果,ngrinder 是个web服务,能做到这一点,也能很好的满足业务需求,入门比较低(开发也可以直接做些简单的压测),也便于管理,大家的东西都放在一个地方。适合自己的才是最好的。

我想请教一下如何监控服务器的cpu,内存等情况呢。我把ngrinder_monitor解压到了服务器root目录下,然后sh run_monitor_bg.sh之后,在新建的测试用例中也添加了服务器ip,但是测试结束后,还是没有服务器的cpu等信息的图形化展示,是我哪一步做的不对么

neven7 #20 · 2016年06月07日 作者

#19楼 @wanxi3 请参考:https://testerhome.com/topics/4279; 你要在配置文件agent.conf 中修改controller端所在的ip和port(默认是13243)。

补充说明:

        // tps 标准差
double tpsStd = new StandardDeviation().evaluate(tpsArray);
// tps 平均值
double tpsMean = new Mean().evaluate(tpsArray, 0, tpsArray.length);
// tps 波动率= tps 标准差 / tps 平均值
double tpsVix = 0;
if(0 != tpsMean){
tpsVix = tpsStd / tpsMean;
}

// meanTestTime 百分位数
Percentile percentile = new Percentile();

以上的三个类无法找到,需要在 ngrinder-core下的pom.xlm中添加:

org.apache.commons
commons-math3
3.4

#16楼 @shijin880921 不会重复下载的,只有第一次的时候会下载。

#20楼 @neven7
monitor中的agent.conf里面的配置有误,@wanxi3 可以参考:https://testerhome.com/topics/5121
monitor.binding_ip=192.168.84.174
monitor.binding_port=13243

neven7 #24 · 2016年06月16日 作者

#21楼 @aizaimenghuangu 恩恩,工具包。

neven7 nGrinder 源码分析:自动中断测试任务 中提及了此贴 12月15日 10:40

@neven7 在ngrinder-core/src/main/java/org/ngrinder/model/PerfTest.java添加tpsStd等信息时碰到org.hibernate.HibernateException: Missing column tpsStd 错误,ngrinder无法启动,请问您在运行过程中遇到了吗?

neven7 #27 · 2016年12月21日 作者

#26楼 @seanshao ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog_schema_H2.xml这个文件中添加了新增字段吗,比如 tpsStd double, 如果确认加了;要把以前的H2中的数据删除(/root/.ngrinder/db),因为以前数据跟现在表字段不兼容。

#27楼 @neven7 删除可以了。

代码按照上面修改的,详细报告是这样的,还有哪里需要注意的吗?

@neven7 使用最新版本3.4进行上述操作,发现新增加的指标在日志中已经体现了(/root/.ngrinder/logs),但是在数据库目录下没有响应的Data生成(/root/.ngrinder/perftest/0_999/1/report),请问你在二次开发过程中是否遇见过这样的情况?

neven7 #32 · 2017年01月07日 作者

#30楼 @seanshao 在ngrinder-controller中src->main->resources里提示语的配置文件,messages_cn.properties(中文),messages_en.properties(英文),messages_kr.properties(韩文)在配置文件添加相应的文本,比如:在messages_cn.properties中:

perfTest.report.successRate=成功率
perfTest.report.tpsStd=TPS \u6807\u51C6\u5DEE
perfTest.report.tpsVix=TPS \u6CE2\u52A8\u7387
perfTest.report.minRT=\u6700\u5C0F RT
perfTest.report.TwentyFiveMeanTime=25 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.FiftyMeanTime=50 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.ServentyFiveMeanTime=75 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.EightyMeanTime=80 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.EightyFiveMeanTime=85 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.NinetyMeanTime=90 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.NinetyFiveMeanTime=95 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.NinetyNineMeanTime=99 \u767E\u5206\u4F4D\u6570 RT
perfTest.report.maxRT=\u6700\u5927 RT
neven7 #33 · 2017年01月07日 作者

#31楼 @seanshao 这些新增数据其实是根据已有数据算出来的,不需要记录到data文件,将计算结果持久到DB中就行,前端展示这些新数据,节省了你计算的成本。

@neven7 非常感谢!

Caused by:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'NGrinderDefaultPluginManager': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.ngrinder.infra.plugin.extension.NGrinderDefaultPluginManager.setExtensionFinder(ro.fortsoft.pf4j.ExtensionFinder); nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'NGrinderDefaultExtensionFinder': Requested bean is currently in creation: Is there an unresolvable circular reference?

谁遇到过这个问题?
我编译出来的包可以在Windows上运行,但是在linux下运行就报错,相同版本的jdk,相同版本的maven

请问这些修改有提pull request吗?每个人一处处改还是挺麻烦的,版本更新了又得改一次

getPlusResult里从meanTestTimeArray取数据的时候需要捕获异常或者做空判断,否则特殊情况下,如果脚本错误(正常般情况下,都会保证验证脚本通过的情况再去执行测试),执行测试的时候,一直会提示Script error但却无法停止测试

数据库的schema变更,建议增加一个 db.changelog_schema_xx.xml来存放,这样就可以利用liquibase在原有数据库基础上做升级而不会丢失原有数据

hexusheng 回复

你好,现在的版本按文章方法更新后会报db异常NGrinderRuntimeException: Exception occurs while Liquibase update DB,只能删除.ngrinder的db文件才能更新成功,也要这种方式解决么?

beyondht2003 回复

具体是不是这个错误记不清了,你可以试下,应该可以的

gatling报告上的Req/s这一栏不就是tps吗
或者gating上的这个数值和ngrinder出来的结果计算方式不同?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册