admin管理员组文章数量:1446760
【连载 21】性能测试实践——超时结账第一回合
3.7.1 超市结账第一回合
让我们把目光转回小八超市。最近生意红火,8个收银台忙得团团转,早高峰时连上厕所的时间都没有。收银员们叫苦不迭,纷纷建议老板临时增加2个收银台。小八思前想后,决定先对现有的8个收银台进行一次摸底,看看在满负荷运转的情况下,每分钟能结账多少顾客。根据摸底结果,再决定是否增加临时收银台。
以此为背景,我们来设计一个性能测试用例。根据需求分析,我们选择线程模型,也就是排队模型,总并发数量为8。测试内容就是模拟顾客结账的流程,简化为三个步骤:扫码计价、付款结账和打包走人。为了给收银员留出热身时间,我们设置了2分钟的Rump-Up时间。
相信大家对这种场景已经驾轻就熟,下面是我设计的多线程类。为了增加一点挑战性,我分别统计了三个步骤的耗时,并且支持异步输出实时信息,这样可以更快定位系统瓶颈,提升排查效率。
既然要增加额外的统计和异步输出功能,必然需要一个额外的线程来完成这个任务。为了避免线程开销,我在TaskExecutor类中增加了一个属性realTimeThread:
代码语言:javascript代码运行次数:0运行复制/**
* 实时信息输出线程,用于实时统计一段时间的TPS和平均耗时
*/
public Thread realTimeThread;
这样在start()方法中稍加改造即可使用。当realTimeThread未赋值时,默认只统计当前实时TPS和RT。
代码语言:javascript代码运行次数:0运行复制if (realTimeThread == null) realTimeThread = new Thread() {
@Override
public void run() {
while (realTimeKey) {
// 重复代码,省略
}
}
};
realTimeThread.start();
接下来我们编写多线程任务类代码。增加了三个阶段方法以及对应的统计属性。本次数据统计采用了实时TPS和RT相同的方案,使用一个全局线程安全对象计算总耗时,然后依据实时TPS计算平均耗时。
为了增加统计数据的波动性,在pay()方法中增加了时间相关变量作为休眠参数。多线程任务类代码如下:
代码语言:javascript代码运行次数:0运行复制package org.funtester.performance.books.chapter03.section7;
import org.funtester.performance.books.chapter03mon.ThreadTool;
import org.funtester.performance.books.chapter03.section3.ThreadTask;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
/**
* 超市收银台性能测试用例
*/
publicclass SupermarketCheckoutTaskFirst extends ThreadTask {
/**
* 计价耗时统计
*/
publicstatic AtomicLong priceCostTime;
/**
* 支付耗时统计
*/
publicstatic AtomicLong payCostTime;
/**
* 打包耗时统计
*/
publicstatic AtomicLong packCostTime;
/**
* 构造方法
* @param totalNum 执行的总次数
*/
public SupermarketCheckoutTaskFirst(int totalNum) {
this.totalNum = totalNum;
this.costTime = new ArrayList<>(totalNum);
priceCostTime = new AtomicLong();
payCostTime = new AtomicLong();
packCostTime = new AtomicLong();
}
/**
* 业务操作,计价、支付、打包
*/
@Override
public void test() {
long start = System.currentTimeMillis();
price();
long price = System.currentTimeMillis();
priceCostTime.addAndGet(price - start);
pay();
long pay = System.currentTimeMillis();
payCostTime.addAndGet(pay - price);
pack();
long pack = System.currentTimeMillis();
packCostTime.addAndGet(pack - pay);
}
/**
* 计价
*/
public void price() {
ThreadTool.sleep(10);
}
/**
* 支付
*/
public void pay() {
ThreadTool.sleep((int) (System.currentTimeMillis() % 10000) / 100);
}
/**
* 打包
*/
public void pack() {
ThreadTool.sleep(10);
}
}
测试用例如下:
代码语言:javascript代码运行次数:0运行复制package org.funtester.performance.books.chapter03.section7;
import org.funtester.performance.books.chapter03mon.ThreadTool;
import org.funtester.performance.books.chapter03.section3.ThreadTask;
import org.funtester.performance.books.chapter03.section4.TaskExecutor;
import java.util.ArrayList;
import java.util.List;
/**
* 超市收银台性能测试用例
*/
publicclass SupermarketCheckoutCase {
public static void main(String[] args) throws InterruptedException {
int total = 1000;
List<ThreadTask> tasks = new ArrayList<>();
for (int i = 0; i < 8; i++) {
SupermarketCheckoutTaskFirst supermarketCheckoutTask = new SupermarketCheckoutTaskFirst(total);
tasks.add(supermarketCheckoutTask);
}
TaskExecutor taskExecutor = new TaskExecutor(tasks, "超市收银台性能测试用例", 120);
Thread thread = new Thread() {
@Override
public void run() {
while (taskExecutor.realTimeKey) {
ThreadTool.sleep(1000);
long sumCost = TaskExecutor.realTimeCostTime.sumThenReset();
long sumTimes = TaskExecutor.realTimeCostTimes.sumThenReset();
System.out.println(String.format("实时统计TPS: %d, 平均耗时: %d", sumTimes, sumTimes == 0 ? 0 : sumCost / sumTimes));
long price = SupermarketCheckoutTaskFirst.priceCostTime.getAndSet(0);
long pay = SupermarketCheckoutTaskFirst.payCostTime.getAndSet(0);
long pack = SupermarketCheckoutTaskFirst.packCostTime.getAndSet(0);
System.out.println(String.format("实时统计各阶段耗时: price: %d, pay: %d, pack: %d", price / sumTimes, pay / sumTimes, pack / sumTimes));
}
}
};
taskExecutor.realTimeThread = thread;
taskExecutor.start();
}
}
控制台输出信息如下(省略了重复内容):
代码语言:javascript代码运行次数:0运行复制实时统计各阶段耗时: price: 11, pay: 51, pack: 11
实时统计TPS: 12, 平均耗时: 84
实时统计各阶段耗时: price: 10, pay: 52, pack: 11
实时统计TPS: 23, 平均耗时: 84
// 其他Rump-Up阶段信息省略
Rump-Up结束,开始执行测试任务!
实时统计TPS: 88, 平均耗时: 90
实时统计各阶段耗时: price: 11, pay: 67, pack: 11
实时统计TPS: 80, 平均耗时: 100
实时统计各阶段耗时: price: 11, pay: 78, pack: 11
任务执行完毕! 预期执行次数: 1000, 实际执行次数 1000, 错误次数 0, 耗时收集数量: 1000
// 此处省略相同多线程任务结束日志
测试TPS: 129, 平均耗时: 62
测试TPS: 129, 总执行次数: 8000
最小值:20
最大值:127
平均值:62
50分位值:57
90分位值:107
95分位值:116
99分位值:124
999分位值:126
任务执行完毕! 压测时长: 62 秒, 预期执行次数: 8000, 实际执行次数 8000, 错误次数 0, 耗时收集数量: 8000
通过这次测试,小八超市的收银台性能一目了然,接下来就是根据数据做决策了。正所谓“磨刀不误砍柴工”,有了这些数据支持,小八的决策会更加科学合理。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-03-10,如有侵权请联系 cloudcommunity@tencent 删除性能测试测试实践统计性能本文标签: 连载 21性能测试实践超时结账第一回合
版权声明:本文标题:【连载 21】性能测试实践——超时结账第一回合 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1748345325a2850082.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论