1. 背景
2. 目的
探究 JMeter 的事务的原子性
3. 实战
说道原子性, 有的同学还一头雾水.... 啥是原子性? 相信大家应该都接触过数据库, 数据库的事务就具有原子性 (其余的几个特性本次不讨论), 宝路觉得这个原子性的概念, 更像是从化学这么学科中 "偷" 过来的. 在化学中, 原子是在化学反应不可再分的微粒. 影射到事务, 那就是一个或者多个操作步骤组成了一个原子单元, 在事务执行过程中, 如果其中任意一个操作步骤失败, 那么整个事务即失败.
说下本次 JMeter 的测试计划, 本次设计 4 个接口,"登录","活动信息","查卡列表","查询余额", 只有登录成功之后才会触发其余交易.
脚本结构 (全部交易默认成功):
脚本中 sampler 都用相同代码来模拟, 可以通过 random 参数控制返回内容, sleep 参数控制交易耗时.
- public class JMeterTransaction extends AbstractJavaSamplerClient {
- @Override
- public Arguments getDefaultParameters() {
- Arguments arguments = new Arguments();
- arguments.addArgument("random","1");
- arguments.addArgument("sleep","50");
- return arguments;
- }
- @Override
- public SampleResult runTest(JavaSamplerContext arg0) {
- SampleResult result = new SampleResult();
- result.sampleStart();
- String random = arg0.getParameter("random");
- long sleep = Long.valueOf(arg0.getParameter("sleep"));
- try {
- TimeUnit.MILLISECONDS.sleep(sleep);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- result.setSuccessful(true);
- result.setResponseCodeOK();
- result.setResponseData(random,"UTF-8");
- result.sampleEnd();
- return result;
- }
- }
10 并发执行 1min 结果:
嗯? 各接口样本数为什么不一样? 有点意思? 理论上每个接口样本数应该一致啊.....
宝路调整了下事务控制器配置, 勾选了 Genernate parent sampler
10 并发执行 1min 结果:
感觉目前没啥问题, 宝路将执行过程中的结果保存了, 打开本次 jtl 结果:
嗯? 如出一辙... 从目前看与事务原子性, 登录成功多少笔, 其余的交易就应该成功多少笔. 目前能看 JMeter 的事务控制不符合原子性, 难道是我使用的姿势有问题?
多次执行过, 宝路发现个共性: 同一个测试计划的各接口的样本总数是递减的. 所以宝路做了推测: 在场景停止时 (线程数从某个设定的值下降到 0), 比如某个线程在执行完 "查询卡列表" 时, 线程就被 kill 了, 进而导致 "查询余额" 接口没被执行.
宝路又做了验证:
不勾选事务控制器 Genernate parent sampler
勾选事务控制器 Genernate parent sampler
清除结果后, 使用聚合报告打开本次执行的 jtl 结果:
经过多次执行, 发了个诡异现象: 在不勾选 Genernate parent sampler 时, 事务控制器统计的样本数与最后一个接口统计的样本数相同, 在勾选 Genernate parent sampler 时, 事务控制器统计的样本数与首个接口统计的样本数相同.
先不管这个是不是 BUG, 从事务角度看不满足原子性. 单纯的事务控制器并不严格的具备原子性, 这是目前看到的表像.
那么怎么解决呢? 宝路之前写过一篇关于 JavaRequest 探究的文章, 感兴趣的同学可以点击看下. 本文就不过多阐述了. 直接看结果
清除聚合报告数据后, 再打开本次执行的 jtl 结果文件
嗯, 真香. 目前觉得唯一的缺点就是没有一个能监听 subResult 的插件. 相同的场景, 都是基于 java 请求的编写的底层脚本, 唯一的区别就是组织方式不同.
其实你完全可以这样看: 把 TestJavaLee 中的 main 看成事务控制器, 你细细品, 再细细品....
进而宝路得出: 单纯用事务控制器来组织多个 sampler, 严格来说不具有事务的原子性.
如果有不同想法或者看法, 欢迎给宝路留言, 最后附下 TestJavaLee 代码:
- public class TestJavaLee extends AbstractJavaSamplerClient {
- @Override
- public Arguments getDefaultParameters() {
- Arguments arguments = new Arguments();
- arguments.addArgument("subResult","RAW_NO");
- return arguments;
- }
- @Override
- public SampleResult runTest(JavaSamplerContext arg0) {
- SampleResult main = new SampleResult();
- main.setSampleLabel("Main 事务");
- main.sampleStart();
- SampleResult sample_a = new SampleResult();
- sample_a.setSampleLabel("登录");
- sample_a.sampleStart();
- try {
- TimeUnit.MILLISECONDS.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- sample_a.sampleEnd();
- sample_a.setSuccessful(true);
- main.addSubResult(sample_a,false);
- SampleResult sample_b = new SampleResult();
- sample_b.setSampleLabel("活动信息");
- sample_b.sampleStart();
- try {
- TimeUnit.MILLISECONDS.sleep(70);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- sample_b.sampleEnd();
- sample_b.setSuccessful(true);
- main.addSubResult(sample_b,false);
- SampleResult sample_c = new SampleResult();
- sample_c.setSampleLabel("查卡列表");
- sample_c.sampleStart();
- try {
- TimeUnit.MILLISECONDS.sleep(80);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- sample_c.sampleEnd();
- sample_c.setSuccessful(true);
- main.addSubResult(sample_c,false);
- SampleResult sample_d = new SampleResult();
- sample_d.setSampleLabel("查询余额");
- sample_d.sampleStart();
- try {
- TimeUnit.MILLISECONDS.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- sample_d.sampleEnd();
- sample_d.setSuccessful(true);
- main.addSubResult(sample_d,false);
- main.setSuccessful(true);
- return main;
- }
- }
来源: https://www.cnblogs.com/leebaul/p/11979227.html