
gatling 的 session 是虚拟用户私有的,无法在不同 scenario 间共享;实现数据流转必须统一在单个用户行为链中完成,而非拆分到多个独立场景。
gatling 的 session 是虚拟用户私有的,无法在不同 scenario 间共享;实现数据流转必须统一在单个用户行为链中完成,而非拆分到多个独立场景。
在 Gatling 性能测试中,一个常见误区是试图将逻辑拆分为多个独立的 ScenarioBuilder(如 BooksStore.getScenario() 和 Client.getScenario()),再通过 .andThen() 串联执行——这并不会实现会话数据的传递。原因在于:Gatling 的 Session 对象严格绑定于单个虚拟用户(Virtual User)的整个生命周期,且每个 Scenario 实际启动的是彼此隔离的用户群体。即使你使用 .andThen(),它也只是按顺序执行两个独立的用户组(例如先 10 个用户执行“获取书籍 ID”,再另 10 个用户执行“查询书籍”),二者 Session 完全不互通,firstBookId 在第二个场景中自然无法解析。
✅ 正确做法:将依赖逻辑封装在同一个 Scenario 内的连续 ChainBuilder 链中,确保数据采集与消费发生在同一虚拟用户的上下文中。
以下为重构后的推荐写法(使用 Scala,Gatling 官方推荐语言;Java DSL 已于 Gatling 3.8+ 正式弃用,故不建议继续使用):
class BookWorkflowSimulation extends Simulation {
val httpProtocol = http
.baseUrl("https://bookylova.com")
.acceptHeader("application/json")
.header("Authorization", getGetAccessToken())
// ✅ 单一场景:一个用户先获取 ID,再用该 ID 查询详情
val bookFlow = scenario("Book Lookup Flow")
.exec(
http("Get Books IDs")
.get("/api/books/ids")
.check(jsonPath("$.idList[0]").saveAs("firstBookId")) // 取第一个 ID(更健壮)
)
.exec(
http("Get Book by ID")
.get("/api/book/${firstBookId}") // Session 变量直接插值
.check(status.is(200))
)
setUp(
bookFlow.inject(
rampUsers(10).during(10.seconds) // 10 个用户在 10 秒内逐步启动
)
).protocols(httpProtocol)
}
? 关键要点说明:
- Session 是线程/用户级隔离的:每个虚拟用户拥有独立 Session 实例,生命周期从 scenario.exec(...) 开始,到该用户完成所有 ChainBuilder 执行后结束;
- .andThen() ≠ 数据传递:它仅表示“先运行 A 场景的所有用户,再运行 B 场景的所有用户”,属于调度顺序控制,不建立 Session 上下文继承关系;
- 跨类复用 ChainBuilder 是可行的,但需保证同场景内调用:你可以将 getBooks 和 readBook 抽取为 object 中的 val 或 def,只要它们被组合进同一个 scenario.exec(...) 链中即可:
object BookFlows {
val getBooks: ChainBuilder = exec(
http("Get Books IDs").get("/api/books/ids")
.check(jsonPath("$.idList[0]").saveAs("firstBookId"))
)
val readBook: ChainBuilder = exec(
http("Get Book by ID").get("/api/book/${firstBookId}")
)
}
// 在 Simulation 中复用:
val scn = scenario("Book Flow").exec(BookFlows.getBooks, BookFlows.readBook)
⚠️ 注意事项:
- 避免全局静态变量或外部存储(如单例类字段)尝试“中转” Session 数据——这不仅线程不安全,更违背 Gatling 设计哲学,会导致压测结果失真;
- 若需多步骤、多角色业务流(如“用户搜索→下单→支付”),应使用 feeders + foreach + doIf 等 DSL 构建条件化流程,而非拆分 Scenario;
- 所有 ${variable} 插值均在运行时由当前 Session 解析,仅当变量已通过 check(...).saveAs(...) 或 session.set(...) 显式写入当前 Session 后才有效。
总结:Gatling 的核心范式是 “一个用户,一条完整业务链”。数据流转不是靠“类间通信”,而是靠 ChainBuilder 的线性组合 + Session 的隐式传递。理解并遵循这一原则,才能写出可维护、可复用、结果可信的性能脚本。










