如何在Gatling/Scala中存储多个用户ID以实现批量删除
保存Gatling创建的用户ID用于后续删除操作
方案一:使用线程安全集合存储ID,同Simulation内完成创建与删除
Gatling是多线程运行环境,必须用线程安全的集合存储所有生成的userId,这里使用ConcurrentLinkedQueue实现全局存储,后续单独的删除场景从集合中取出ID执行删除。
修改后的代码示例:
import io.gatling.core.Predef._ import io.gatling.http.Predef._ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import scala.util.Random import java.util.concurrent.ConcurrentLinkedQueue class CreateAndBulkDeleteUser extends Simulation { // 全局线程安全队列,存储所有创建的userId val userIdsQueue = new ConcurrentLinkedQueue[String]() // 协议配置 val httpProtocol = http .baseUrl("https://redacted.com") var timestamp = Iterator .continually { Map("timestamp" -> LocalDateTime.now.format(DateTimeFormatter.ofPattern("YYYYMMdd_hhmmss"))) } var randomName = Iterator .continually { Map("random" -> Random.alphanumeric.take(6).mkString) } // 创建用户场景:仅负责生成用户并保存ID到全局队列 val createUserScn = scenario("Create Users") .feed(randomName) .feed(timestamp) .exec( http("Create new user") .post("/users") .header("content-type", "application/json") .body(StringBody( """{ | "firstName": "${random}", | "lastName": "${timestamp}", | "email": "redacted+${random}@gmail.com", |}""".stripMargin)).asJson .check( status is 200, jsonPath("$.userId").saveAs("userId"))) .exec(session => { val userId = session("userId").as[String] userIdsQueue.add(userId) println(s"已创建用户ID: $userId") session }) // 删除用户场景:从全局队列取出ID执行删除 val deleteUserScn = scenario("Delete Users") .exec(session => { val userId = userIdsQueue.poll() if (userId != null) { session.set("userId", userId) } else { session.markAsFailed // 队列空时终止会话 } }) .exec( http("Delete user by userId") .delete("/users/${userId}") // 需根据实际API调整路径,原脚本未携带userId参数 .check(status is 200)) // 执行配置:先完成100个用户创建,再执行删除 setUp( createUserScn.inject(rampUsers(100).during(60)), deleteUserScn.inject(nothingFor(65), atOnceUsers(100)) // 等待创建完成后启动删除 ).protocols(httpProtocol) }
关键改动说明:
- 新增
ConcurrentLinkedQueue确保多线程环境下安全读写用户ID - 拆分原场景为创建场景和删除场景,分工明确
- 修正删除请求路径,补充userId参数(需匹配实际API要求)
- 通过
nothingFor控制删除场景在创建流程结束后执行
方案二:将ID保存到文件,供其他脚本读取使用
如果需要在不同Simulation脚本中复用用户ID,可以将ID写入本地文件,后续删除脚本读取文件内容作为数据源。
创建用户并写入文件的脚本:
import io.gatling.core.Predef._ import io.gatling.http.Predef._ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import scala.util.Random import java.io.{BufferedWriter, FileWriter} import java.util.concurrent.locks.ReentrantLock class CreateUsersAndSaveIds extends Simulation { // 文件写入锁,避免多线程写入冲突 val fileLock = new ReentrantLock() val userIdFile = "./user_ids.txt" val httpProtocol = http .baseUrl("https://redacted.com") var timestamp = Iterator .continually { Map("timestamp" -> LocalDateTime.now.format(DateTimeFormatter.ofPattern("YYYYMMdd_hhmmss"))) } var randomName = Iterator .continually { Map("random" -> Random.alphanumeric.take(6).mkString) } val createUserScn = scenario("Create Users and Save IDs") .feed(randomName) .feed(timestamp) .exec( http("Create new user") .post("/users") .header("content-type", "application/json") .body(StringBody( """{ | "firstName": "${random}", | "lastName": "${timestamp}", | "email": "redacted+${random}@gmail.com", |}""".stripMargin)).asJson .check( status is 200, jsonPath("$.userId").saveAs("userId"))) .exec(session => { val userId = session("userId").as[String] // 加锁写入文件 fileLock.lock() try { val writer = new BufferedWriter(new FileWriter(userIdFile, true)) writer.write(s"$userId\n") writer.close() } finally { fileLock.unlock() } println(s"已保存用户ID到文件: $userId") session }) setUp( createUserScn.inject(rampUsers(100).during(60)) ).protocols(httpProtocol) }
读取文件ID执行删除的脚本:
import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.io.Source class DeleteUsersFromFile extends Simulation { val httpProtocol = http .baseUrl("https://redacted.com") // 读取文件中的userId,转换为Gatling Feeder val userIdFeeder = Source.fromFile("./user_ids.txt") .getLines() .map(userId => Map("userId" -> userId)) .toList .queue // 按顺序消费ID,用完即止 val deleteUserScn = scenario("Delete Users from File") .feed(userIdFeeder) .exec( http("Delete user by userId") .delete("/users/${userId}") .check(status is 200)) setUp( deleteUserScn.inject(atOnceUsers(userIdFeeder.size)) // 根据文件中ID数量设置并发数 ).protocols(httpProtocol) }
关键说明:
- 使用
ReentrantLock避免多线程写入文件时内容混乱 - 读取文件时将每行ID转换为Feeder,供删除场景批量使用
- 确保两个脚本运行在同一工作目录下,保证文件路径有效
内容的提问来源于stack exchange,提问作者xRADxRYANx
相关产品推荐
相关产品推荐

