You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 12:15:36