Gatling中如何基于单用户会话链,正确实现跨场景会话变量传递?
- 内容介绍
- 相关推荐
本文共计704个文字,预计阅读时间需要3分钟。
Gatling的session代表虚拟用户(VU)的私有、有生命周期的状态容器,无法在不同scenario或不同虚拟用户间共享。要实现先获取id,再使用id请求详情的流程,必须将两个操作序列联接在同一scenario中,而不是作为独立的场景执行。
在 Gatling 中,Session 是每个虚拟用户独享的上下文对象,其生命周期始于该用户进入 Scenario,终于该用户退出模拟。当你在 BooksStore.getScenario() 中通过 .check(jsonPath("$.idList[1]").saveAs("firstBookId")) 保存变量时,该值仅存于执行该请求的当前虚拟用户 Session 中;而 Client.getScenario() 启动的是另一批(甚至全新)虚拟用户,它们的 Session 完全隔离——因此 ${firstBookId} 在 readBook 链中必然解析失败,返回空或报错。
✅ 正确做法:构建单一流程链(Single User Journey)
应将“获取图书 ID”与“根据 ID 查询图书详情”作为同一虚拟用户的连续行为,用 exec(...).exec(...) 串接:
// 推荐写法:统一在 Simulation 类中定义可复用的 ChainBuilder 对象 object BookWorkflows { val getBooksIds: ChainBuilder = exec( http("Get Books IDs") .get("/api/books/ids") .check(jsonPath("$.idList[1]").saveAs("firstBookId")) ) val readFirstBook: ChainBuilder = exec( http("Get Book by ID") .get("/api/book/${firstBookId}") .check(status.is(200)) ) } // 在 Simulation 中组合使用 class MySimulation extends Simulation { val httpProtocol = http .baseUrl("https://bookylova.com") .acceptHeader("application/json") .header("Authorization", getGetAccessToken()) val scn = scenario("Book Discovery Flow") .exec(BookWorkflows.getBooksIds) .pause(1) // 可选:模拟用户自然等待 .exec(BookWorkflows.readFirstBook) setUp( scn.inject(rampUsers(10).during(10.seconds)) ).protocols(httpProtocol) }
? 进阶提示:
- 若需复用逻辑但避免硬编码,可将 ChainBuilder 封装为 Scala object 的 val 成员(如上例),而非 Java 风格的静态方法——Gatling 原生推荐 Scala DSL,且 ChainBuilder 是函数式不可变结构,天然适合组合复用。
- 如需多级依赖(例如:获取 ID → 获取 Token → 查询详情),可使用 .feed() + csvFeeder 或 .sessionAttribute() 配合自定义函数扩展 Session,但核心原则不变:所有依赖步骤必须属于同一虚拟用户的执行链。
- 切勿尝试用静态变量、单例类或外部缓存跨 VU 传递 Session 数据——这不仅违反 Gatling 设计哲学,更会导致线程安全问题、数据污染和压测结果失真。
总结:Gatling 不是“多个测试用例的集合”,而是“一个用户行为旅程的建模”。把业务流当做一个完整故事来写,而非割裂成原子测试,才能真正发挥其异步高并发、状态可追踪的优势。
本文共计704个文字,预计阅读时间需要3分钟。
Gatling的session代表虚拟用户(VU)的私有、有生命周期的状态容器,无法在不同scenario或不同虚拟用户间共享。要实现先获取id,再使用id请求详情的流程,必须将两个操作序列联接在同一scenario中,而不是作为独立的场景执行。
在 Gatling 中,Session 是每个虚拟用户独享的上下文对象,其生命周期始于该用户进入 Scenario,终于该用户退出模拟。当你在 BooksStore.getScenario() 中通过 .check(jsonPath("$.idList[1]").saveAs("firstBookId")) 保存变量时,该值仅存于执行该请求的当前虚拟用户 Session 中;而 Client.getScenario() 启动的是另一批(甚至全新)虚拟用户,它们的 Session 完全隔离——因此 ${firstBookId} 在 readBook 链中必然解析失败,返回空或报错。
✅ 正确做法:构建单一流程链(Single User Journey)
应将“获取图书 ID”与“根据 ID 查询图书详情”作为同一虚拟用户的连续行为,用 exec(...).exec(...) 串接:
// 推荐写法:统一在 Simulation 类中定义可复用的 ChainBuilder 对象 object BookWorkflows { val getBooksIds: ChainBuilder = exec( http("Get Books IDs") .get("/api/books/ids") .check(jsonPath("$.idList[1]").saveAs("firstBookId")) ) val readFirstBook: ChainBuilder = exec( http("Get Book by ID") .get("/api/book/${firstBookId}") .check(status.is(200)) ) } // 在 Simulation 中组合使用 class MySimulation extends Simulation { val httpProtocol = http .baseUrl("https://bookylova.com") .acceptHeader("application/json") .header("Authorization", getGetAccessToken()) val scn = scenario("Book Discovery Flow") .exec(BookWorkflows.getBooksIds) .pause(1) // 可选:模拟用户自然等待 .exec(BookWorkflows.readFirstBook) setUp( scn.inject(rampUsers(10).during(10.seconds)) ).protocols(httpProtocol) }
? 进阶提示:
- 若需复用逻辑但避免硬编码,可将 ChainBuilder 封装为 Scala object 的 val 成员(如上例),而非 Java 风格的静态方法——Gatling 原生推荐 Scala DSL,且 ChainBuilder 是函数式不可变结构,天然适合组合复用。
- 如需多级依赖(例如:获取 ID → 获取 Token → 查询详情),可使用 .feed() + csvFeeder 或 .sessionAttribute() 配合自定义函数扩展 Session,但核心原则不变:所有依赖步骤必须属于同一虚拟用户的执行链。
- 切勿尝试用静态变量、单例类或外部缓存跨 VU 传递 Session 数据——这不仅违反 Gatling 设计哲学,更会导致线程安全问题、数据污染和压测结果失真。
总结:Gatling 不是“多个测试用例的集合”,而是“一个用户行为旅程的建模”。把业务流当做一个完整故事来写,而非割裂成原子测试,才能真正发挥其异步高并发、状态可追踪的优势。

