
Gatling 的 Session 是虚拟用户(VU)私有的、有生命周期的状态容器,无法在不同 Scenario 或不同虚拟用户间共享;要实现“先获取 ID 再使用 ID 请求详情”的流程,必须将两个操作串联在同一 Scenario 的 ChainBuilder 链中,而非拆分为独立场景。
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 不是“多个测试用例的集合”,而是“一个用户行为旅程的建模”。把业务流当做一个完整故事来写,而非割裂成原子测试,才能真正发挥其异步高并发、状态可追踪的优势。










