面向開發的測試技術(二):性能測試

NO IMAGE

引子:自上世紀末Kent Beck提出TDD(Test-Driven Development)開發理念以來,開發和測試的邊界變的越來越模糊,從原本上下游的依賴關係,逐步演變成你中有我、我中有你的互賴關係,甚至很多公司設立了新的QE(Quality Engineer)職位。和傳統的QA(Quality Assurance)不同,QE的主要職責是通過工程化的手段保證項目質量,這些手段包括但不僅限於編寫單元測試、集成測試,搭建自動化測試流程,設計性能測試等。可以說,QE身上兼具了QA的質量意識和開發的工程能力。我會從開發的角度分三期聊聊QE這個亦測試亦開發的角色所需的基本技能,這篇是第二篇。

前情概要:

1 什麼是性能測試?

先來看一下維基百科裡對性能測試的定義,

In software engineering, performance testing is in general, a testing practice performed to determine how a system performs in terms of responsiveness and stability under a particular workload. – Wikipedia

注意上述定義中有三個關鍵詞:

  • responsiveness,即響應時間,請求發出去之後,服務端需要多久才能返回結果,顯然響應時間越短,性能越好。
  • stability,即穩定性,同樣的請求,不同時刻發出去,響應時間差別越小,穩定性越好,性能也越好。
  • workload,即負載,同一時刻服務端收到的請求數量,其中單位時間內成功處理的請求數量即吞吐量,吞吐量越大,性能越好。

響應時間和吞吐量是衡量應用性能好壞最重要的兩個指標。對於絕大多數應用,剛開始的時候,響應時間最短;隨著負載的增大,吞吐量快速上升,響應時間也逐漸變長;當負載超過某一個值之後,響應時間會突然呈指數級放大,同時吞吐量也應聲下跌,應用性能急劇下降,整個過程如下:

面向開發的測試技術(二):性能測試

圖片出處:性能測試應該怎麼做?

2 性能測試的目的

瞭解了應用性能變化的普遍規律,性能測試的目的也就有了答案:針對某一應用,找出響應時間和吞吐量的量化關係,找到應用性能變化的臨界點。你可能會問,知道了這些有什麼用呢?在我看來,至少有3個層面的好處:

第一,有的放矢,提高資源利用率。性能測試的過程就是量化性能的過程,有了各種性能數據,你才能對應用性能進行定量分析,找到並解決潛在的性能問題,從而提高資源利用率。

第二,科學的進行容量規劃。找到了應用性能變化的臨界點,也就很容易找到單節點的性能極限,這是進行容量規劃的重要決策依據。比如某一應用在單節點下的極限吞吐量是2000 QPS,那麼面對10000 QPS的流量,至少需要部署5個節點。

第三,改善QoS(Quality of Service)。很多時候,資源是有限的,面對超出服務能力的流量,為了保證QoS,必須做出取捨(比如限流降級,開關預案等),應用性能數據是設計QoS方案的重要依據。

3 性能測試的三個常見誤區

誤區1:只看平均值,不懂TP95/TP99

用平均值來衡量響應時間是性能測試中最常見的誤區。從第1小節的插圖可以看出,隨著吞吐量的增大,響應時間會逐漸變長,當達到最大吞吐量之後,響應時間會開始加速上升,尤其是排在後面的請求。在這個時刻,如果只看平均值,你往往察覺不到問題,因為大部分請求的響應時間還是很短的,慢請求只佔一個很小的比例,所以平均值變化不大。但實際上,可能已經有超過1%,甚至5%的請求的響應時間已經超出設計的範圍了。

更科學、更合理的指標是看TP95或者TP99響應時間。TP是Top Percentile的縮寫,是一個統計學術語,用來描述一組數值的分佈特徵。以TP95為例,假設有100個數字,從小到大排序之後,第95個數字的值就是這組數字的TP95值,表示至少有95%的數字是小於或者等於這個值。

以一次具體的性能測試為例,

面向開發的測試技術(二):性能測試

面向開發的測試技術(二):性能測試

總共有1000次請求,平均響應時間是58.9ms,TP95是123.85ms(平均響應時間的2.1倍),TP99是997.99ms(平均響應時間的16.9倍)。假設應用設計的最大響應時間是100ms,單看平均時間是完全符合要求的,但實際上已經有超過50個請求失敗了。如果看TP95或者TP99,問題就很清楚了。

誤區2:只關注響應時間和吞吐量,忽視請求成功率

雖說衡量應用性能好壞最主要是看響應時間和吞吐量,但這裡有個大前提,所有請求(如果做不到所有,至少也要絕大多數請求,比如99.9%)都被成功處理了,而不是返回一堆錯誤碼。如果不能保證這一點,那麼再低的響應時間,再高的吞吐量都是沒有意義的。

誤區3:忘了測試端也存在性能瓶頸

性能測試的第三個誤區是隻關注服務端,而忽略了測試端本身可能也存在限制。比如測試用例設置了10000併發數,但實際運行用例的機器最大隻支持5000併發數,如果只看服務端的數據,你可能會誤以為服務端最大就只支持5000併發數。如果遇到這種情況,或者換用更高性能的測試機器,或者增加測試機器的數量。

4 如何進行性能測試?

介紹完性能測試相關的一些概念之後,再來看一下有哪些工具可以進行性能測試。

4.1 JMeter

JMeter可能是最常用的性能測試工具。它既支持圖形界面,也支持命令行,屬於黑盒測試的範疇,對非開發人員比較友好,上手也非常容易。圖形界面一般用於編寫、調試測試用例,而實際的性能測試建議還是在命令行下運行。

面向開發的測試技術(二):性能測試

併發設置

面向開發的測試技術(二):性能測試

請求參數

面向開發的測試技術(二):性能測試

結果報表

命令行下的常用命令:

  • 設置JVM參數:JVM_ARGS=”-Xms2g -Xmx2g”
  • 運行測試:jmeter -n -t <jmx_file>
  • 運行測試同時生成報表:jmeter -n -t <jmx_file> -l <log_file> -e -o <report_dir>

除了JMeter,其他常用的性能測試工具還有ab, http_load, wrk以及商用的LoaderRunner

4.2 JMH

如果測試用例比較複雜,或者負責性能測試的人員具有一定的開發能力,也可以考慮使用一些框架編寫單獨的性能測試程序。對於Java開發人員而言,JMH是一個推薦的選擇。類似於JUnit,JMH提供了一系列註解用於編寫測試用例,以及一個運行測試的引擎。事實上,即將發佈的JDK 9默認就會包含JMH。

下面是我GitHub上的示例工程裡的一個例子,

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@Threads(Threads.MAX)
@State(Scope.Benchmark)
@Warmup(iterations = 1, time = 3)
@Measurement(iterations = 3, time = 3)
public class VacationClientBenchmark {
private VacationClient vacationClient;
@Setup
public void setUp() {
VacationClientConfig clientConfig = new VacationClientConfig("http://localhost:3000");
vacationClient = new VacationClient(clientConfig);
}
@Benchmark
public void benchmarkIsWeekend() {
VacationRequest request = new VacationRequest();
request.setType(PERSONAL);
OffsetDateTime lastSunday = OffsetDateTime.now().with(TemporalAdjusters.previous(SUNDAY));
request.setStart(lastSunday);
request.setEnd(lastSunday.plusDays(1));
Asserts.isTrue(vacationClient.isWeekend(request).isSuccess());
}
// 僅限於IDE中運行
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(VacationClientBenchmark.class.getSimpleName())
.build();
new Runner(opt).run();
}
}

其中:

  • @BenchmarkMode: 性能測試模式,支持Throughput,AverageTime,SingleShotTime等多種模式。
  • @Fork: 設置運行性能測試的Fork進程數,默認是0,表示共用JMH主進程。
  • @Threads: 併發數,Threads.MAX表示同系統的CPU核數。
  • @Warmup和@Measurement: 分別設置預熱和實際性能測試的運行輪數,每輪持續的時間等
  • @Setup和@Benchmark: 等同於JUnit裡的@BeforeClass和@Test

在命令行下,使用JMH框架編寫的性能測試程序只能以Jar包的形式運行(Main函數固定為org.openjdk.jmh.Main),因此一般會針對每個JMH程序單獨維護一個項目。如果是Maven項目,可以使用官方提供的jmh-java-benchmark-archetype,如果是Gradle項目,可以使用jmh-gradle-plugin插件。

4 小結

以上就是我對性能測試的一些見解,歡迎你到我的留言板分享,和大家一起過過招。下一篇我將聊一下Web的自動化測試,敬請期待。

5 參考

相關文章

【Spring5】響應式Web框架實戰(上)

響應式編程總覽

【Spring5】響應式Web框架前瞻

面向開發的測試技術(三):Web自動化測試