tags:
- 中间件
Apache JMeter 是一款基于 Java 的开源工具,专为负载测试和性能测试设计。最初用于测试 Web 应用程序,但现已扩展到其他测试功能。它支持多种协议和应用类型,包括 HTTP、HTTPS、SOAP、REST、FTP、数据库、LDAP 等,适用于静态和动态资源的性能测试。
核心功能
JMeter 提供了强大的功能,包括多线程框架、动态 HTML 报告生成、数据分析插件以及脚本化采样器(支持 Groovy 和 BeanShell)。它还支持通过命令行模式进行无界面测试,适用于各种操作系统。
性能测试执行
配置线程组参数(如并发用户数和持续时间),点击运行按钮启动测试。测试完成后,可通过聚合报告分析关键指标,如平均响应时间、吞吐量和错误率。
TPS
首先,TPS(Transactions Per Second,事务每秒),通常用于衡量系统在单位时间内成功处理的事务数量。
事务,往往包含多个操作步骤或数据库变更,强调完整性的处理单元及其成功提交。
一个事务,是客户端向服务器端发送请求后,服务器端完成该请求的整个处理过程。
比如:一个下单包含:
下单接口 + 扣库存 + 写订单表,→ 成功完成 1 次,计 1 TPS。
TPS, 常用于评估系统在事务性工作负载下的吞吐能力。
QPS
其次,QPS(Queries Per Second,查询每秒),原本多用于衡量查询类请求的处理速率。
比如:每秒处理 10,000 次 HTTP 请求;
→ QPS = 10,000
尤其在搜索、缓存或数据库读操作场景下常见。
QPS 更侧重于单次请求或查询的数量,对于无状态的、读多于写的场景尤为适用。
在很多工程实践中,QPS 与 TPS 的差别取决于"请求"是否等同于"事务"。
并发数
最后,并发数(Concurrency),指的是系统在同一时刻同时存在的活动请求数、或连接数。
三者的关系
可概括为:并发数决定了系统同时处理请求的规模,进而影响在单位时间内能完成的事务或查询数(即 TPS/QPS)。
而 TPS/QPS ,则是衡量随时间推移系统吞吐能力的指标。
QPS 和 TPS 在很多接口级压测场景下,可以近似等同,但严格含义有差别。
一次"页面访问 / 下单流程",通常算 1 个 TPS。
这次访问可能产生多个后端请求(查商品、查库存、写订单等),这些请求的总次数是 QPS。
运行:在bin文件夹下.bat结尾的文件,双击运行

切换语言

创建 线程组



HTTP请求默认值


简单请求:通过设置请求默认值后,只需要进行http请求添加


请求结果

配置cookie:注意设置作用域,此项目通过设置userTicket进行用户信息访问

点击绿色按钮进行运行


配置多用户进行访问
18501345678,7d4e8707cd2c49a7a2e31c4836a0d3f9
18504720060,85a4645be4c348558bb6c7075612535c
配置多用户规则
代码生成用户登录信息(userTicket)
java
/**
* 生成用户
* @param count 生成用户数量
* @return 生成结果
*/
@RequestMapping("/generateUsers")
public CommonResult<String> generateUsers(HttpServletRequest request, HttpServletResponse response,
@RequestParam(defaultValue = "10") int count) throws IOException {
FileWriter writer = new FileWriter("config.txt");
for (int i = 0; i < count; i++) {
String mobile = "13000000000" + i;
TUser user = new TUser();
user.setId(13000000000L + i);
user.setNickname(mobile);
user.setPassword(MD5Util.inputPassToDbPass("123456", "1a2b3c4d"));
user.setSlat("1a2b3c4d");
user.setHead("");
user.setRegisterDate(LocalDateTime.now());
user.setLastLoginDate(LocalDateTime.now());
user.setLoginCount(1);
userService.save(user);
// 调用登录接口获取userTicket
LoginVo loginVo = new LoginVo();
loginVo.setMobile(mobile);
loginVo.setPassword(MD5Util.inputPassToFormPass("123456")); // 使用表单密码
CommonResult<String> loginResult = userService.login(request, response, loginVo);
if (loginResult.getCode() == 200) { // 登录成功
String userTicket = loginResult.getData();
// 写入文件:手机号,userTicket
writer.write(mobile + "," + userTicket + "\n");
}
}
writer.close();
return CommonResult.success("成功生成" + count + "个用户,并将信息写入config.txt");
}



1000次访问


10000


秒杀商品测试
配置秒杀线程组



汇总报告

并发出现库存超卖

测试商品访问接口
1000并发 2轮

redis缓存数据后 1000并发 2轮
java
@RequestMapping(value="/toList")
@ResponseBody
public CommonResult<Map<String,Object>> toList(TUser user) {
Map<String,Object> map = new HashMap<>();
ValueOperations valueOperations = redisTemplate.opsForValue();
//Redis中获取页面,如果不为空,直接返回页面
String goodsList = (String) valueOperations.get("goodsVo");
if (!StringUtils.isEmpty(goodsList)) {
map.put("goodsVo", goodsList);
return CommonResult.success(map);
}
if (user == null)
return CommonResult.error(ResponseCode.SESSION_ERROR);
map.put("user", user);
List<GoodsVo> goodsVo = goodsService.findGoodsVo();
map.put("goodsVo", JsonUtil.object2JsonStr(goodsVo));
if (goodsVo != null)
{
valueOperations.set("goodsVo", JsonUtil.object2JsonStr(goodsVo), 60, java.util.concurrent.TimeUnit.SECONDS);
}
//
// GoodsVo goodsVoByGoodsId = goodsService.findGoodsVoByGoodsId(1L);
// map.put("goods", goodsVoByGoodsId);
return CommonResult.success(map);
}
吞吐量1788.9

参考资料