在性能测试中,实时展示测试数据是一个非常重要的功能。它可以帮助测试人员实时监控系统的性能表现,及时发现性能瓶颈或异常情况,从而做出相应的调整或停止测试,避免对系统造成不必要的损害。为了实现这一功能,我们需要对现有的性能测试引擎进行进一步的升级。
实时统计 TPS(每秒事务数)和平均耗时:在测试过程中,实时展示系统的 TPS 和平均响应时间,帮助测试人员了解系统的当前负载和性能表现。
异步线程监控:通过一个独立的异步线程,定期收集和展示性能数据,确保主线程的性能测试任务不受影响。
动态控制监控线程:在测试结束后,能够优雅地关闭监控线程,避免资源浪费。
增加实时统计属性:
LongAdder
来记录实时总耗时和总次数。LongAdder
在高并发场景下性能优于AtomicLong
或AtomicInteger
。realTimeKey
),用于控制监控线程的启停。创建监控线程:
start()
方法中创建一个独立的线程,用于定期收集和展示实时性能数据。多线程任务类上报数据:
run()
方法中,每次执行测试任务后,将耗时和执行次数上报到执行类的LongAdder
属性中。优雅关闭监控线程:
realTimeKey
设置为false
,并等待监控线程结束。以下是实现实时展示功能的关键代码片段:
public class TaskExecutor {
// 用于实时统计一段时间总耗时
public static LongAdder realTimeCostTime = new LongAdder();
// 用于实时统计一段时间总次数
public static LongAdder realTimeCostTimes = new LongAdder();
// 用于控制实时统计线程结束开关
public boolean realTimeKey = true;
/**
* 开始执行任务
*/
public void start() throws InterruptedException {
// 创建实时统计线程
Thread realTimeThread = new Thread(() -> {
while (realTimeKey) {
ThreadTool.sleep(1000); // 休眠1秒
long sumCost = realTimeCostTime.sumThenReset(); // 重置总耗时
long sumTimes = realTimeCostTimes.sumThenReset(); // 重置总次数
System.out.println(String.format("实时统计TPS: %d, 平均耗时: %d",
sumTimes, sumTimes == 0 ? 0 : sumCost / sumTimes));
}
});
realTimeThread.start();
// 启动Rump-Up阶段
int gap = rumpUpTime * 1000 / tasks.size(); // 计算每个线程的启动间隔
for (ThreadTask task : tasks) {
poolExecutor.execute(task); // 提交线程池执行
ThreadTool.sleep(gap); // 休眠,间隔提交多线程任务
}
rumpUpCountDownLatch.await(); // 等待Rump-Up计数器为0
tasks.forEach(f -> f.countState = true); // 开启数据收集
System.out.println("Rump-Up结束, 开始执行测试任务!");
this.startTimestamp = System.currentTimeMillis(); // 记录开始时间
stopCountDownLatch.await(); // 等待停止任务计数器为0
this.endTimestamp = System.currentTimeMillis(); // 记录结束时间
// 关闭线程池和实时统计线程
this.poolExecutor.shutdown();
realTimeKey = false; // 关闭实时统计线程
realTimeThread.join(); // 等待实时统计线程结束
handleData(); // 处理数据
}
}
public class ThreadTask implements Runnable {
@Override
public void run() {
rumpUpCountDownLatch.countDown(); // 计数器减一
try {
before(); // 前置处理
while (true) {
if (ABORT.get() || needStop || executeNum >= totalNum) {
break; // 判断是否终止测试任务
}
try {
long start = System.currentTimeMillis(); // 记录开始时间
test(); // 测试方法
long end = System.currentTimeMillis(); // 记录结束时间
int delay = (int) (end - start);
// 上报实时数据
TaskExecutor.realTimeCostTime.add(delay);
TaskExecutor.realTimeCostTimes.add(1);
if (countState) {
executeNum++; // 记录执行次数
costTime.add(delay); // 记录耗时
}
} catch (Exception e) {
if (countState) errorNum++; // 记录错误次数
e.printStackTrace();
}
}
after(); // 后置处理
} catch (Exception e) {
e.printStackTrace();
} finally {
stopCountDownLatch.countDown(); // 计数器减一
}
}
}
public class TestEngineDemo {
public static void main(String[] args) throws InterruptedException {
int tasksNum = 2; // 任务数,即并发数量
int totalNum = 30; // 单个任务的总执行次数
List<ThreadTask> tasks = new ArrayList<>(); // 任务集合
for (int i = 0; i < tasksNum; i++) {
ThreadTask threadTask = new ThreadTask() {
@Override
public void test() {
ThreadTool.sleep(100); // 模拟业务操作
}
};
threadTask.totalNum = totalNum; // 设置任务的总执行次数
threadTask.costTime = new ArrayList<>(totalNum); // 设置任务的执行时间集合
tasks.add(threadTask); // 将任务添加到任务集合
}
new TaskExecutor(tasks, "性能测试引擎演示", 4).start(); // 创建并发任务执行器并启动
}
}
在测试过程中,控制台会实时输出类似以下内容:
实时统计TPS: 9, 平均耗时: 101
实时统计TPS: 10, 平均耗时: 102
实时统计TPS: 20, 平均耗时: 102
实时统计TPS: 19, 平均耗时: 102
Rump-Up结束, 开始执行测试任务!
实时统计TPS: 19, 平均耗时: 102
实时统计TPS: 20, 平均耗时: 103
实时统计TPS: 20, 平均耗时: 101
通过增加实时展示功能,性能测试引擎能够更好地满足实际测试需求。测试人员可以实时监控系统的性能表现,及时发现并解决问题,从而更高效地完成性能测试任务。
FunTester 原创精华
【连载】从 Java 开始性能测试
故障测试与 Web 前端
服务端功能测试
性能测试专题
Java、Groovy、Go
白盒、工具、爬虫、UI 自动化
理论、感悟、视频