使用 Gatling 負載測試 Rest 端點
一、概述
在本文中,我們將探討如何使用 Gatling 在任何 Rest 端點上進行性能測試,特別側重於負載測試。我們將從快速介紹各種類型的性能測試及其關鍵性能指標 (KPI) 開始。
接下來,我們將快速概述 Gatling 術語。我們將使用 Maven Gatling 插件和依賴項設置示例。我們將探索Gatling Java DSL來執行模擬場景的負載測試。
最後,我們將運行模擬並查看生成的報告。
2. 性能測試類型
性能測試涉及測量各種指標以了解系統在不同級別的流量和吞吐量下的性能。其他類型的性能測試包括負載測試、壓力測試、浸泡測試、尖峰測試和可伸縮性測試。接下來讓我們快速了解每種性能測試策略的目的。
負載測試涉及在一段時間內在並發虛擬用戶的重負載下測試系統。另一方面,壓力測試涉及逐漸增加系統的負載以找到其突破點。浸泡測試旨在使系統的流量在較長時間內保持穩定,以識別瓶頸。顧名思義,尖峰測試包括測試當請求數量快速增加到壓力水平,然後很快再次減少時系統的性能。最後,可擴展性測試涉及測試當用戶請求數量增加或減少時系統的性能。
在進行性能測試時,我們可以收集幾個關鍵性能指標(KPI)來衡量系統性能。這些包括transaction response times, throughput
(一段時間內處理的事務數)和錯誤(例如超時) 。壓力測試還可以幫助識別內存洩漏、速度減慢、安全漏洞和數據損壞。
在本文中,我們將重點介紹使用 Gatling 進行負載測試。
3. 關鍵術語
讓我們從 Gatling 框架的一些基本術語開始。
- 場景:場景是虛擬用戶為複制常見用戶操作(例如登錄或購買)而採取的一系列步驟。
- Feeders:Feeders 是允許數據從外部源(例如 CSV 或 JSON 文件)輸入到虛擬用戶操作中的機制。
- 模擬:模擬確定在特定時間範圍內運行場景的虛擬用戶數量
- 會話:每個虛擬用戶都由一個會話支持,該會話跟踪場景期間交換的消息。
- Recorder:Gatling 的 UI 提供了一個 Recorder 工具,可以生成 Scenario 和 Simulation
4. 示例設置
讓我們關注Employee management
微服務的一小部分,它由一個帶有必須進行負載測試的 POST 和 GET 端點的 RestController 組成。
在我們開始實施我們的簡單解決方案之前,讓我們添加所需的Gatling 依賴項:
<dependency>
<groupId>io.gatling</groupId>
<artifactId>gatling-app</artifactId>
<version>3.7.2</version>
</dependency>
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>3.7.2</version>
</dependency>
接下來,讓我們添加maven 插件:
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>4.2.9</version>
<configuration>
<simulationClass>org.baeldung.EmployeeRegistrationSimulation</simulationClass>
</configuration>
</plugin>
如 pom.xml 文件中的信息所示,我們已通過插件配置將模擬類顯式設置為EmployeeRegistationSimulation
。這意味著插件將利用指定的類作為運行模擬的基礎。
接下來,讓我們使用我們想要使用 Gatling 進行負載測試的 POST 和 GET 端點來定義我們的RestController
:
@PostMapping(consumes = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Void> addEmployee(@RequestBody EmployeeCreationRequest request, UriComponentsBuilder uriComponentsBuilder) {
URI location = uriComponentsBuilder.path("/api/employees/{id}")
.buildAndExpand("99")
.toUri();
return ResponseEntity.created(location)
.build();
}
接下來讓我們添加 GET 端點:
@GetMapping("/{id}")
public Employee getEmployeeWithId(@PathVariable("id") Long id) {
List<Employee> allEmployees = createEmployees();
return allEmployees.get(ThreadLocalRandom.current()
.nextInt(0, allEmployees.size()));
}
接下來,讓我們擴展Simulation
類及其支持負載測試的組成組件和 API。
五、仿真步驟
模擬代表捕獲各個方面的負載測試,例如多個用戶群體可能如何操作、他們將執行什麼場景以及如何注入新的虛擬用戶。在 Gatling 框架中, Simulation
類是啟動負載測試過程的主要組件。 Gatling Java API 包括可變抽像類Simulation
。我們可以根據我們的特定要求擴展Simulation
類以創建自定義模擬:
Public class EmployeeRegistrationSimulation extends Simulation {
private static final HttpProtocolBuilder HTTP_PROTOCOL_BUILDER = setupProtocolForSimulation();
private static final Iterator<Map<String, Object>> FEED_DATA = setupTestFeedData();
private static final ScenarioBuilder POST_SCENARIO_BUILDER = buildPostScenario();
// ...
}
基本上在這裡,我們需要定義以下內容:
- HTTP協議配置
- 標頭
- 餵食器
- HTTP 請求
- 設想
- 負載注入模式
現在,讓我們看看各個步驟以及如何使用 Gatling 提供的 DSL 來定義它們。接下來我們將從協議配置開始。
5.1. HTTP 協議配置
Gatling 是一種與技術無關的負載測試工具,它支持各種協議,包括 HTTP、HTTPS 和 WebSockets。本節將重點介紹為我們的負載測試場景配置 HTTP 協議。
要在EmployeeRegistrationSimulation
類中設置 HTTP 協議詳細信息,我們將使用HttpDsl
類型,它用作 Gatling HTTP DSL 的入口點。然後我們將使用HTTPProtocolBuilder
DSL 來定義HTTP
協議配置:
private static HttpProtocolBuilder setupProtocolForSimulation() {
return HttpDsl.http.baseUrl("http://localhost:8080")
.acceptHeader("application/json")
.maxConnectionsPerHost(10)
.userAgentHeader("Gatling/Performance Test");
}
在 Gatling 中配置 HTTP 協議涉及使用HttpDsl
類來定義使用HTTPProtocolBuilder
DSL 的 HTTP 協議配置。關鍵配置設置包括 baseUrl、 acceptHeader, maxConnectionsPerHost, and userAgentHeader.
這些設置有助於確保我們的負載測試準確地模擬真實場景。
5.2.饋線定義
Feeders 是一個方便的API
,允許測試人員將來自外部源的數據注入到虛擬用戶會話中。 Gatling 支持各種饋線,例如 CSV、JSON、基於文件和基於數組/列表的饋線。
接下來,讓我們創建一個方法來返回測試用例的測試數據:
private static Iterator<Map<String, Object>> feedData() {
Faker faker = new Faker();
Iterator<Map<String, Object>> iterator;
iterator = Stream.generate(() -> {
Map<String, Object> stringObjectMap = new HashMap<>();
stringObjectMap.put("empName", faker.name()
.fullName());
return stringObjectMap;
})
.iterator();
return iterator;
}
在這裡,我們將創建一個返回Iterator<Map<String, Object>>
的方法來為我們的測試用例檢索測試數據。然後,方法feedData()
使用Faker
庫生成測試數據,創建一個 HashMap 來存儲數據,並返回數據的迭代器。
feeder 本質上是由feed
方法創建的Iterator<Map<String, Object>>
組件的類型別名。 feed
方法輪詢Map<String, Object>
記錄並將其內容注入模擬場景。
Gatling 還提供了內置的 feeder 策略,例如queue(), random(), shuffle(), and circular()
。此外,根據被測系統,我們可以將數據加載機製配置為eager()
或batch()
。
5.3.場景定義
Gatling 中的場景表示虛擬用戶將遵循的典型用戶行為。它是一個基於Employee
Controller 中定義的資源的工作流。在這種情況下,我們將創建一個場景,使用簡單的工作流程模擬員工創建:
private static ScenarioBuilder buildPostScenario() {
return CoreDsl.scenario("Load Test Creating Employee")
.feed(FEED_DATA)
.exec(http("create-employee-request").post("/api/employees")
.header("Content-Type," "application/json")
.body(StringBody("{ \"empName\": \"${empName}\" }"))
.check(status().is(201))
.check(header("Location").saveAs("location")))
.exec(http("get-employee-request").get(session -> session.getString("location"))
.check(status().is(200)));
}
Gatling API 提供了scenario(String name)
方法,該方法返回ScenarioBuilder
類的一個實例。 ScenarioBuilder
封裝了場景的詳細信息,包括測試數據的來源和 HTTP 請求詳細信息,例如請求正文、標頭和預期狀態代碼。
本質上,我們正在構建一個場景,在該場景中,我們首先使用post
方法發送請求,通過發送包含從測試數據中檢索到的empName
值的JSON r
請求正文來創建員工。該方法還檢查預期的 HTTP 狀態代碼 (201) 並使用saveAs
方法將 Location 標頭值保存到會話中。
第二個請求使用get
方法通過將保存的 Location 標頭值發送到請求 URL 來檢索創建的員工。它還會檢查預期的 HTTP 狀態代碼 (200)。
5.4.負載注入模型
除了定義場景和協議之外,我們還必須為我們的模擬定義負載注入模式。在我們的示例中,我們將通過隨著時間的推移添加虛擬用戶來逐漸增加負載。 Gatling 提供了兩種負載注入模型:open 和 closed 。開放模型允許我們控制虛擬用戶的到達率,這更能代表現實生活中的系統。
Gatling 的 Java API 提供了一個名為OpenInjectionStep
的類,它封裝了開放注入工作負載模式的公共屬性和行為。我們可以使用OpenInjectionStep
的三個子類:
-
ConstantRateOpenInjection:
這種注入模型保持虛擬用戶的恆定到達率。 -
RampRateOpenInjection:
這種注入模型逐漸增加虛擬用戶的到達率。 -
Composite:
這種注入模型允許我們組合不同類型的注入模式。
對於我們的示例,讓我們使用RampRateOpenInjection
。我們將從 50 個虛擬用戶開始,逐漸增加負載,每 30 秒添加 50 個用戶,直到達到 200 個虛擬用戶。然後我們將負載保持在 200 個虛擬用戶 5 分鐘:
private RampRateOpenInjectionStep postEndpointInjectionProfile() {
int totalDesiredUserCount = 200;
double userRampUpPerInterval = 50;
double rampUpIntervalSeconds = 30;
int totalRampUptimeSeconds = 120;
int steadyStateDurationSeconds = 300;
return rampUsersPerSec(userRampUpPerInterval / (rampUpIntervalSeconds / 60)).to(totalDesiredUserCount)
.during(Duration.ofSeconds(totalRampUptimeSeconds + steadyStateDurationSeconds));
}
通過定義負載注入模式,我們可以準確地模擬我們的系統在不同負載水平下的行為。這有助於我們識別性能瓶頸並確保我們的系統能夠處理預期的用戶負載。
5.5.設置模擬器
為了設置模擬,我們將結合我們之前定義的協議、場景和負載注入模型。這個設置將從EmployeeRegistrationSimulation
類的構造函數中調用:
public EmployeeRegistrationSimulation() {
setUp(BUILD_POST_SCENARIO.injectOpen(postEndpointInjectionProfile())
.protocols(HTTP_PROTOCOL_BUILDER));
}
5.6.斷言
最後,我們將使用 Gatling DSL 斷言模擬按預期工作。讓我們回到我們的EmployeeRegistrationSimulation()
構造函數並向現有的setup(..)
方法添加一些斷言:
setUp(BUILD_POST_SCENARIO.injectOpen(postEndpointInjectionProfile())
.protocols(HTTP_PROTOCOL_BUILDER))
.assertions(
global().responseTime().max().lte(10000),
global().successfulRequests().percent().gt(90d)
如我們所見,這裡我們要斷言以下條件:
- 基於設置的最大響應時間應小於或等於 10 秒
- 成功請求的百分比應大於 90
6. 運行模擬和報告分析
當我們創建 Gatling 項目時,我們將 Maven 與Gatling Maven Plugin一起使用。因此,我們可以使用 Maven 任務來執行我們的 Gatling 測試。要通過 Maven 運行 Gatling 腳本,請在 Gatling 項目的文件夾中打開命令提示符:
mvn gatling:test
因此,我們將收集以下指標:
最後,Gatling 在target/gatling
目錄中生成一個 HTML 報告。此目錄中的主要文件是index.html
,它匯總了負載測試配置、響應時間分佈圖表以及每個請求的統計信息,如上所述。讓我們來看看報告中的一些圖表:
每秒請求數圖表幫助我們了解系統如何處理不斷增加的流量水平。通過分析每秒請求數圖,我們可以確定係統在不降低性能或導致錯誤的情況下可以運行的最佳請求數。此信息可用於提高系統的可擴展性並確保它能夠處理預期的流量水平。
另一個有趣的圖表是響應時間範圍:
響應時間分佈圖顯示了屬於特定響應時間段的請求的百分比,例如小於 800 毫秒、介於 800 毫秒和 1.2 秒之間以及大於 1.2 秒。我們可以看到我們所有的響應都在 <800 毫秒內。
讓我們看看響應時間百分位數:
請求統計部分顯示了每個請求的詳細信息,包括請求數量、成功請求的百分比以及各種響應時間百分位數,例如第 50、75、95 和 99 個百分位數。總體而言, index.html
文件提供了負載測試結果的綜合摘要,可以輕鬆識別性能瓶頸或問題。
七、結論
在本文中,我們學習了使用 Gatling Java DSL 對任何 REST 端點進行負載測試。
首先,我們簡要概述了不同類型的性能測試。接下來,我們介紹了 Gatling 特有的關鍵術語。我們演示瞭如何在 POST 端點上實施負載測試,同時遵守所需的注入負載和時間限制。此外,我們可以分析測試結果以確定需要改進和優化的領域。
與往常一樣,可以在 GitHub 上找到本文的完整源代碼。