如何在Gatling不同类的ChainBuilder间传递收集的变量
Gatling跨测试类传递会话变量的解决方案
核心问题本质
Gatling的会话对象是用户绑定、线程隔离的,每个虚拟用户拥有独立会话,跨类直接访问其他用户的会话必然无效;而跨场景默认不共享会话,所以你当前用andThen串联场景的方式,仅能保证场景执行顺序,但无法直接传递单个用户的会话变量。
可行解决方案
1. 将相关Chain合并到同一个场景(推荐)
这是最直接的方案,让同一个虚拟用户的会话贯穿两个Chain,自然就能访问到之前保存的变量。
示例代码:
// BooksStore类 class BooksStore { val fetchFirstBookId: ChainBuilder = exec(http("Get Book List") .get("/api/books") .check(jsonPath("$[0].id").saveAs("firstBookId"))) } // Client类 class Client { val queryBookDetail: ChainBuilder = exec(http("Get Book Detail") .get("/api/books/${firstBookId}")) } // Simulation类 class BookFlowSimulation extends Simulation { val booksStore = new BooksStore() val client = new Client() // 同一个场景中先后执行两个Chain,共享同一用户会话 val bookScenario = scenario("End-to-End Book Flow") .exec(booksStore.fetchFirstBookId) .exec(client.queryBookDetail) setUp(bookScenario.inject(atOnceUsers(5))) }
2. 跨场景传递全局共享数据(适合静态/唯一数据)
如果需要在不同场景间传递全局唯一的数据(比如系统中第一个图书ID,所有后续用户都用这个值),可以用线程安全的全局存储:
// 全局共享对象,用线程安全容器保证多线程环境下的数据一致性 object SharedBookData { private val bookIdHolder = new java.util.concurrent.ConcurrentHashMap[String, String]() def setFirstBookId(id: String): Unit = bookIdHolder.put("firstBookId", id) def getFirstBookId: Option[String] = Option(bookIdHolder.get("firstBookId")) } // BooksStore类 class BooksStore { val fetchAndStoreBookId: ChainBuilder = exec(http("Get Book List") .get("/api/books") .check(jsonPath("$[0].id").saveAs("firstBookId"))) .exec(session => { val bookId = session("firstBookId").as[String] SharedBookData.setFirstBookId(bookId) session }) } // Client类 class Client { val useGlobalBookId: ChainBuilder = exec(session => { SharedBookData.getFirstBookId match { case Some(id) => session.set("firstBookId", id) case None => session.markAsFailed // 处理数据未准备好的情况 } }) .exec(http("Query Book Detail") .get("/api/books/${firstBookId}")) } // Simulation类 class CrossScenarioSimulation extends Simulation { val booksStore = new BooksStore() val client = new Client() val fetchScenario = scenario("Fetch Global Book Id").exec(booksStore.fetchAndStoreBookId) val queryScenario = scenario("Use Global Book Id").exec(client.useGlobalBookId) // 用andThen保证第一个场景执行完成后,再启动第二个场景 setUp(fetchScenario.inject(atOnceUsers(1)) andThen queryScenario.inject(atOnceUsers(10))) }
3. 用Feeder批量传递数据(适合多组复用数据)
如果需要收集一组数据供后续场景使用,可以先将数据导出到文件,再用Feeder加载:
// BooksStore类:收集数据并写入文件 class BooksStore { val exportBookIds: ChainBuilder = exec(http("Get All Books") .get("/api/books") .check(jsonPath("$[*].id").findAll.saveAs("bookIds"))) .exec(session => { val bookIds = session("bookIds").as[List[String]] val writer = new java.io.PrintWriter("book_ids.csv") writer.println("bookId") bookIds.foreach(id => writer.println(id)) writer.close() session }) } // Client类:从Feeder读取数据 class Client { val bookIdFeeder = csv("book_ids.csv").circular val queryWithFeeder: ChainBuilder = feed(bookIdFeeder) .exec(http("Query Book Detail") .get("/api/books/${bookId}")) } // Simulation类 class FeederBasedSimulation extends Simulation { val booksStore = new BooksStore() val client = new Client() val exportScenario = scenario("Export Book Ids").exec(booksStore.exportBookIds) val queryScenario = scenario("Query Books from Feeder").exec(client.queryWithFeeder) setUp(exportScenario.inject(atOnceUsers(1)) andThen queryScenario.inject(rampUsers(20).during(10))) }
避坑提醒
- 不要尝试用普通类的成员变量存储会话值:Gatling的测试代码是初始化一次后多线程运行,成员变量会被所有用户共享,导致数据混乱。
- 跨场景传递用户专属会话变量:这种场景不存在合理的实现方式,因为每个用户的会话是独立的,跨场景意味着不同用户实例,会话无法共享。
内容的提问来源于stack exchange,提问作者Razvan
相关产品推荐
相关产品推荐

