Gatling 性能测试实战案例
电商系统登录与商品浏览
场景描述
模拟1000个用户同时登录电商平台并浏览商品详情页。

完整代码示例
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
import java.time.Duration;
public class ECommerceSimulation extends Simulation {
// HTTP 协议配置
HttpProtocolBuilder httpProtocol = http
.baseUrl("https://www.example-ecommerce.com")
.acceptHeader("application/json")
.userAgentHeader("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 测试数据 - 用户信息
Iterator<Map<String, Object>> userFeeder =
Iterator.continually(Map.of(
"username", "user" + RandomStringUtils.random(5, true, false) + "@test.com",
"password", "test123"
));
// 场景1: 登录场景
ScenarioBuilder loginScenario = scenario("用户登录")
.feed(userFeeder)
.exec(http("打开登录页面")
.get("/login")
.check(status().is(200)))
.pause(1, 3)
.exec(http("提交登录请求")
.post("/api/login")
.header("Content-Type", "application/json")
.body(StringBody("{\"username\":\"#{username}\",\"password\":\"#{password}\"}"))
.check(jsonPath("$.token").saveAs("authToken"))
.check(status().is(200)))
.pause(2)
.exec(session -> {
System.out.println("用户 " + session.getString("username") + " 登录成功");
return session;
});
// 场景2: 商品浏览场景
ScenarioBuilder browseScenario = scenario("商品浏览")
.exec(http("获取商品列表")
.get("/api/products")
.queryParam("page", "1")
.queryParam("size", "20")
.check(jsonPath("$.total").exists()))
.pause(2)
.exec(http("查看商品详情")
.get("/api/products/${productId}")
.check(status().in(200, 404)))
.repeat(3) {
pause(1)
.exec(http("浏览商品图片")
.get("/api/products/images/${imageId}"))
};
// 负载配置
SetUp setUp = setUp(
loginScenario.injectOpen(
rampUsers(100).during(Duration.ofSeconds(10)),
constantUsersPerSec(50).during(Duration.ofMinutes(5))
),
browseScenario.injectOpen(
rampUsers(500).during(Duration.ofSeconds(30)),
constantUsersPerSec(100).during(Duration.ofMinutes(10))
)
).protocols(httpProtocol);
}
RESTful API 压力测试
场景描述
对订单创建、查询、更新API进行持续压力测试。
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
public class OrderApiStressTest extends Simulation {
// 环境配置
private static final String BASE_URL = "https://api.example.com/v1";
private static final String API_KEY = "your-api-key";
// HTTP 协议
HttpProtocolBuilder httpProtocol = http
.baseUrl(BASE_URL)
.header("Authorization", "Bearer ${token}")
.header("X-API-Key", API_KEY)
.contentTypeHeader("application/json")
.check(status().is(200));
// 创建订单场景
ScenarioBuilder createOrder = scenario("创建订单")
.exec(session -> session.set("orderId", UUID.randomUUID().toString()))
.exec(http("创建订单")
.post("/orders")
.body(ElFileBody("data/create_order.json"))
.check(jsonPath("$.id").saveAs("orderId"))
.check(jsonPath("$.status").is("created")))
.pause(1)
.exec(http("查询订单")
.get("/orders/${orderId}")
.check(jsonPath("$.id").is("${orderId}")));
// 批量更新场景
ScenarioBuilder updateOrder = scenario("更新订单状态")
.exec(http("获取待处理订单")
.get("/orders")
.queryParam("status", "pending")
.check(jsonPath("$.orders[*].id").findRandom().saveAs("pendingOrderId")))
.pause(2)
.exec(http("更新订单")
.put("/orders/${pendingOrderId}")
.body(StringBody("{\"status\":\"processing\"}"))
.check(jsonPath("$.status").is("processing")));
// 并发测试配置
SetUp setUp = setUp(
createOrder.injectOpen(
rampUsers(50).during(10),
constantUsersPerSec(20).during(120),
rampUsers(100).during(30)
),
updateOrder.injectOpen(
rampUsers(30).during(10),
constantUsersPerSec(10).during(120)
)
).protocols(httpProtocol)
.assertions(
global().responseTime().max().lt(2000),
global().successfulRequests().percent().gt(95.0),
forAll().responseTime().percentile3().lt(1500)
);
}
WebSocket 实时通信测试
场景描述
测试即时通讯系统中的消息发送和接收。
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.core.structure.ScenarioBuilder
import scala.concurrent.duration._
class WebSocketSimulation extends Simulation {
val httpProtocol = http
.baseUrl("wss://ws.example-chat.com")
.acceptHeader("application/json")
.userAgentHeader("Gatling-Perf-Test")
// WebSocket 连接
val connectWs = exec(
ws("建立WebSocket连接")
.connect("/chat/room/1")
.await(5 seconds)(
ws.checkTextMessage("连接确认")
.check(regex("connected"))
)
)
// 发送消息场景
val sendMessage = exec(
ws("发送文本消息")
.sendTextMessage("""{"type":"message","content":"Hello from Gatling!"}""")
)
val receiveMessage = exec(
ws("接收回复")
.await(10 seconds)(
ws.checkTextMessage("收到回复")
.check(regex("message_id"))
)
)
val chatScenario = scenario("实时聊天")
.exec(connectWs)
.pause(2)
.repeat(10) {
exec(sendMessage)
.pause(500 milliseconds)
.exec(receiveMessage)
}
.exec(ws("关闭连接").close)
setUp(
chatScenario.inject(
rampUsers(200).during(30 seconds),
constantUsersPerSec(50) during (2 minutes)
)
).protocols(httpProtocol)
}
文件上传/下载测试
场景描述
测试文件上传功能和批量下载功能。
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import java.util.UUID
class FileTransferSimulation extends Simulation {
val httpProtocol = http
.baseUrl("https://files.example.com")
.acceptHeader("application/json")
.userAgentHeader("Gatling-File-Test")
// 文件上传场景
val uploadScenario = scenario("文件上传")
.exec(session =>
session.set("fileName", s"test_${UUID.randomUUID()}.pdf")
.set("fileSize", 1024 * 1024) // 1MB
)
.exec(
http("上传文件")
.post("/upload")
.header("Content-Type", "multipart/form-data")
.bodyPart(RawFileBodyPart("file", "test_data.pdf")
.fileName("${fileName}")
.contentType("application/pdf"))
.check(status().is(200))
.check(jsonPath("$.fileId").saveAs("fileId"))
)
.pause(2)
.exec(
http("下载文件")
.get("/download/${fileId}")
.check(
status().is(200),
header("Content-Length").is("${fileSize}")
)
)
// 批量下载场景
val batchDownload = scenario("批量下载")
.exec { session =>
val fileIds = (1 to 10).map(i => s"file_${i}").toArray
session.set("batchFiles", fileIds.mkString(","))
}
.exec(
http("批量下载文件")
.post("/batch-download")
.body(StringBody("""{"files":["${batchFiles}"]}"""))
.check(status().is(200))
)
setUp(
uploadScenario.inject(
rampUsers(100).during(60 seconds),
constantUsersPerSec(10) during (5 minutes)
),
batchDownload.inject(
rampUsers(50).during(30 seconds),
constantUsersPerSec(5) during (5 minutes)
)
).protocols(httpProtocol)
}
数据库关联查询场景
场景描述
模拟用户进行复杂查询和结果分页浏览。
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class ComplexQuerySimulation extends Simulation {
val httpProtocol = http
.baseUrl("https://search.example.com")
.acceptHeader("application/json")
.contentTypeHeader("application/json")
// 搜索参数数据源
val searchTerms = Array("手机", "电脑", "耳机", "相机", "平板")
val priceRanges = Array("0-500", "500-1000", "1000-2000", "2000-5000")
val sortOrders = Array("relevance", "price_asc", "price_desc", "rating")
// 复杂搜索场景
val searchScenario = scenario("复杂搜索")
.exec { session =>
val term = searchTerms(Random.nextInt(searchTerms.length))
val price = priceRanges(Random.nextInt(priceRanges.length))
val sort = sortOrders(Random.nextInt(sortOrders.length))
session
.set("searchTerm", term)
.set("priceRange", price)
.set("sortOrder", sort)
}
.exec(
http("执行搜索")
.get("/api/search")
.queryParam("q", "${searchTerm}")
.queryParam("price", "${priceRange}")
.queryParam("sort", "${sortOrder}")
.queryParam("page", "1")
.queryParam("size", "20")
.check(
jsonPath("$.total").saveAs("totalResults"),
jsonPath("$.results[*].id").findAll.saveAs("productIds")
)
)
.pause(1)
.exec { session =>
val total = session("totalResults").as[Int]
val totalPages = Math.min(Math.ceil(total/20.0).toInt, 10)
session.set("totalPages", totalPages)
}
.exec { session =>
val totalPages = session("totalPages").as[Int]
val currentPage = Random.nextInt(totalPages) + 1
session.set("currentPage", currentPage)
}
.exec(
http("分页查询")
.get("/api/search")
.queryParam("q", "${searchTerm}")
.queryParam("page", "${currentPage}")
.queryParam("size", "20")
.check(jsonPath("$.page").is("${currentPage}"))
)
// 产品详情查看
val productDetail = scenario("产品详情")
.repeat(5) {
exec(
http("获取热门产品")
.get("/api/products/hot")
.check(jsonPath("$[*].id").findRandom.saveAs("hotProductId"))
)
.pause(2)
.exec(
http("查看产品详情")
.get("/api/products/${hotProductId}")
.check(
jsonPath("$.reviews").exists(),
jsonPath("$.specifications").exists()
)
)
.pause(1)
.exec(
http("获取相关推荐")
.get("/api/products/${hotProductId}/recommendations")
.check(jsonPath("$[*].id").exists())
)
}
setUp(
searchScenario.inject(
rampUsers(100).during(30 seconds),
constantUsersPerSec(20) during (10 minutes)
),
productDetail.inject(
rampUsers(50).during(30 seconds),
constantUsersPerSec(10) during (10 minutes)
)
).protocols(httpProtocol)
}
测试数据文件示例
create_order.json
{
"customerId": "${customerId}",
"products": [
{
"productId": "PRD001",
"quantity": 2,
"unitPrice": 199.99
},
{
"productId": "PRD002",
"quantity": 1,
"unitPrice": 349.50
}
],
"shippingAddress": {
"street": "123 Test Street",
"city": "Beijing",
"postalCode": "100000",
"country": "China"
},
"paymentMethod": "credit_card",
"notes": "Priority delivery requested"
}
测试报告解读与常用断言
// 断言示例
setUp(
// 场景定义...
).assertions(
// 响应时间断言
global.responseTime.max.lt(5000), // 最大响应时间小于5秒
global.responseTime.mean.lt(1000), // 平均响应时间小于1秒
// 成功率断言
global.successfulRequests.percent.gt(99), // 成功率大于99%
// 分位数断言
global.responseTime.percentile1.lt(500), // 50%响应时间<500ms
global.responseTime.percentile2.lt(1500), // 75%响应时间<1500ms
global.responseTime.percentile3.lt(3000), // 95%响应时间<3000ms
global.responseTime.percentile4.lt(4000), // 99%响应时间<4000ms
// 吞吐量断言
global.requestsPerSec.gt(500), // 每秒请求数大于500
// 并发数断言
global.concurrentUsers.min.gt(100), // 最小并发用户数大于100
global.concurrentUsers.max.lt(1000) // 最大并发用户数小于1000
)
最佳实践建议
参数化设计
- 使用feeder从外部文件读取测试数据
- 避免硬编码数据,使用随机化
- 数据和测试逻辑分离
断言设计
- 验证业务成功标准(登录成功标志)
- 验证数据完整性(JSON路径检查)
- 性能阈值定义(P95 < 2秒)
监控指标
.manage(metrics {
export {
prometheus {
port = 9095
}
}
})
报告输出
.enableReports {
html {
outputDirectory = "target/gatling-reports"
openOnExit = false
}
json {
path = "target/gatling-reports/results.json"
}
}
性能调优建议
- 测试前预热服务
- 合理设置超时时间
- 适时清理测试数据
- 监控服务器资源使用情况
这些案例覆盖了常见的性能测试场景,按需取用,实际项目中应根据具体需求进行定制和优化。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播