如何让Gatling的exec操作按顺序执行?
问题:Gatling脚本执行顺序异常,部分操作随机跳过
我用Java编写了如下Gatling脚本(Scala下表现一致):
scenario("testScenario") .exec(Actions.characters) .exec(characterById) .exec(image) .exec(location) .exec(random) .exec(getResidents) .foreach("#{residents}", "resident") .on(Actions.getEachResident);
Actions类代码如下:
static class Actions { static ChainBuilder characters = exec( http("character") .get("/character?page=2") .check(jsonPath("$.results[?(@.name=='" + NAME + "')].id").saveAs("id"))); static ChainBuilder characterById = exec(http("characterById") .get("/character/#{id}") .check(jsonPath("$.location.url").saveAs("location")) .check(jsonPath("$.image").saveAs("image"))); static ChainBuilder image = exec(http("image") .get("#{image}")); static ChainBuilder location = exec(http("location") .get("#{location}") .check(jsonPath("$.residents[*]").findAll().saveAs("res")) .check(jsonPath("$.residents[*]").findRandom().saveAs("randomResident"))); static ChainBuilder random = exec(http("random") .get("#{randomResident}")); static ChainBuilder getResidents = exec(session -> { List<String> residents = session.getList("res"); return session.set("residents", residents); }); static final ChainBuilder getEachResident = exec(http("Each resident").get("#{resident}")); }
调试发现residents列表包含3个元素:
"https://rickandmortyapi.com/api/character/23" "https://rickandmortyapi.com/api/character/204" "https://rickandmortyapi.com/api/character/320"
多次运行脚本结果不一致:有时跳过random操作,仅执行2次resident请求;有时执行random操作,但仅执行1次resident请求。我怀疑Gatling未按顺序执行最后两个exec操作,但exec链本应是顺序执行的。请问我哪里出错了?如何让Gatling的exec操作按顺序执行?
问题原因与解决方案
核心问题
Gatling的foreach是独立的结构,你当前的写法把它和前面的exec链平级了,导致执行引擎调度时出现不确定性,进而引发操作跳过、循环次数异常等问题。
修复方法
需要明确foreach是前面所有exec步骤的后续逻辑,有两种实现方式:
方式1:用then()明确链式调用关系
修改脚本最后部分,通过then()把foreach绑定为前面步骤的后续:
scenario("testScenario") .exec(Actions.characters) .exec(characterById) .exec(image) .exec(location) .exec(random) .exec(getResidents) .then( foreach("#{residents}", "resident") .on(Actions.getEachResident) );
方式2:将foreach包裹在exec中
把foreach作为一个独立的执行步骤嵌入链中:
scenario("testScenario") .exec(Actions.characters) .exec(characterById) .exec(image) .exec(location) .exec(random) .exec(getResidents) .exec( foreach("#{residents}", "resident") .on(Actions.getEachResident) );
额外优化:简化冗余步骤
你当前的getResidents只是把res变量重命名为residents,完全可以在location步骤中直接存储目标变量,省去多余的Session操作:
static ChainBuilder location = exec(http("location") .get("#{location}") .check(jsonPath("$.residents[*]").findAll().saveAs("residents")) // 直接存为residents .check(jsonPath("$.residents[*]").findRandom().saveAs("randomResident")));
修改后可以直接删除exec(getResidents)步骤,脚本更简洁,也减少了出错概率。
内容的提问来源于stack exchange,提问作者Debugger
相关产品推荐
相关产品推荐

