引子:自上世紀(jì)末Kent Beck提出TDD(Test-Driven Development)開發(fā)理念以來爆侣,開發(fā)和測(cè)試的邊界變的越來越模糊,從原本上下游的依賴關(guān)系雪猪,逐步演變成你中有我、我中有你的互賴關(guān)系,甚至很多公司設(shè)立了新的QE(Quality Engineer)職位纤泵。和傳統(tǒng)的QA(Quality Assurance)不同,QE的主要職責(zé)是通過工程化的手段保證項(xiàng)目質(zhì)量镜粤,這些手段包括但不僅限于編寫單元測(cè)試捏题、集成測(cè)試,搭建自動(dòng)化測(cè)試流程肉渴,設(shè)計(jì)性能測(cè)試等公荧。可以說同规,QE身上兼具了QA的質(zhì)量意識(shí)和開發(fā)的工程能力循狰。我會(huì)從開發(fā)的角度分三期聊聊QE這個(gè)亦測(cè)試亦開發(fā)的角色所需的基本技能,這篇是第二篇券勺。
前情概要:
1 什么是性能測(cè)試绪钥?
先來看一下維基百科里對(duì)性能測(cè)試的定義,
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
注意上述定義中有三個(gè)關(guān)鍵詞:
- responsiveness关炼,即響應(yīng)時(shí)間程腹,請(qǐng)求發(fā)出去之后,服務(wù)端需要多久才能返回結(jié)果儒拂,顯然響應(yīng)時(shí)間越短寸潦,性能越好。
- stability社痛,即穩(wěn)定性甸祭,同樣的請(qǐng)求,不同時(shí)刻發(fā)出去褥影,響應(yīng)時(shí)間差別越小池户,穩(wěn)定性越好,性能也越好凡怎。
- workload校焦,即負(fù)載,同一時(shí)刻服務(wù)端收到的請(qǐng)求數(shù)量统倒,其中單位時(shí)間內(nèi)成功處理的請(qǐng)求數(shù)量即吞吐量寨典,吞吐量越大,性能越好房匆。
響應(yīng)時(shí)間和吞吐量是衡量應(yīng)用性能好壞最重要的兩個(gè)指標(biāo)耸成。對(duì)于絕大多數(shù)應(yīng)用报亩,剛開始的時(shí)候,響應(yīng)時(shí)間最短井氢;隨著負(fù)載的增大弦追,吞吐量快速上升,響應(yīng)時(shí)間也逐漸變長花竞;當(dāng)負(fù)載超過某一個(gè)值之后劲件,響應(yīng)時(shí)間會(huì)突然呈指數(shù)級(jí)放大,同時(shí)吞吐量也應(yīng)聲下跌约急,應(yīng)用性能急劇下降零远,整個(gè)過程如下:
圖片出處:性能測(cè)試應(yīng)該怎么做?
2 性能測(cè)試的目的
了解了應(yīng)用性能變化的普遍規(guī)律厌蔽,性能測(cè)試的目的也就有了答案:針對(duì)某一應(yīng)用牵辣,找出響應(yīng)時(shí)間和吞吐量的量化關(guān)系,找到應(yīng)用性能變化的臨界點(diǎn)奴饮。你可能會(huì)問服猪,知道了這些有什么用呢?在我看來拐云,至少有3個(gè)層面的好處:
第一,有的放矢近她,提高資源利用率叉瘩。性能測(cè)試的過程就是量化性能的過程,有了各種性能數(shù)據(jù)粘捎,你才能對(duì)應(yīng)用性能進(jìn)行定量分析薇缅,找到并解決潛在的性能問題,從而提高資源利用率攒磨。
第二泳桦,科學(xué)的進(jìn)行容量規(guī)劃。找到了應(yīng)用性能變化的臨界點(diǎn)娩缰,也就很容易找到單節(jié)點(diǎn)的性能極限灸撰,這是進(jìn)行容量規(guī)劃的重要決策依據(jù)。比如某一應(yīng)用在單節(jié)點(diǎn)下的極限吞吐量是2000 QPS拼坎,那么面對(duì)10000 QPS的流量浮毯,至少需要部署5個(gè)節(jié)點(diǎn)。
第三泰鸡,改善QoS(Quality of Service)债蓝。很多時(shí)候,資源是有限的盛龄,面對(duì)超出服務(wù)能力的流量饰迹,為了保證QoS芳誓,必須做出取舍(比如限流降級(jí),開關(guān)預(yù)案等)啊鸭,應(yīng)用性能數(shù)據(jù)是設(shè)計(jì)QoS方案的重要依據(jù)锹淌。
3 性能測(cè)試的三個(gè)常見誤區(qū)
誤區(qū)1:只看平均值,不懂TP95/TP99
用平均值來衡量響應(yīng)時(shí)間是性能測(cè)試中最常見的誤區(qū)莉掂。從第1小節(jié)的插圖可以看出葛圃,隨著吞吐量的增大,響應(yīng)時(shí)間會(huì)逐漸變長憎妙,當(dāng)達(dá)到最大吞吐量之后库正,響應(yīng)時(shí)間會(huì)開始加速上升,尤其是排在后面的請(qǐng)求厘唾。在這個(gè)時(shí)刻褥符,如果只看平均值,你往往察覺不到問題抚垃,因?yàn)榇蟛糠终?qǐng)求的響應(yīng)時(shí)間還是很短的喷楣,慢請(qǐng)求只占一個(gè)很小的比例,所以平均值變化不大鹤树。但實(shí)際上铣焊,可能已經(jīng)有超過1%,甚至5%的請(qǐng)求的響應(yīng)時(shí)間已經(jīng)超出設(shè)計(jì)的范圍了罕伯。
更科學(xué)曲伊、更合理的指標(biāo)是看TP95或者TP99響應(yīng)時(shí)間。TP是Top Percentile的縮寫追他,是一個(gè)統(tǒng)計(jì)學(xué)術(shù)語坟募,用來描述一組數(shù)值的分布特征。以TP95為例邑狸,假設(shè)有100個(gè)數(shù)字懈糯,從小到大排序之后,第95個(gè)數(shù)字的值就是這組數(shù)字的TP95值单雾,表示至少有95%的數(shù)字是小于或者等于這個(gè)值赚哗。
以一次具體的性能測(cè)試為例,
總共有1000次請(qǐng)求硅堆,平均響應(yīng)時(shí)間是58.9ms蜂奸,TP95是123.85ms(平均響應(yīng)時(shí)間的2.1倍),TP99是997.99ms(平均響應(yīng)時(shí)間的16.9倍)硬萍。假設(shè)應(yīng)用設(shè)計(jì)的最大響應(yīng)時(shí)間是100ms扩所,單看平均時(shí)間是完全符合要求的,但實(shí)際上已經(jīng)有超過50個(gè)請(qǐng)求失敗了朴乖。如果看TP95或者TP99祖屏,問題就很清楚了助赞。
誤區(qū)2:只關(guān)注響應(yīng)時(shí)間和吞吐量,忽視請(qǐng)求成功率
雖說衡量應(yīng)用性能好壞最主要是看響應(yīng)時(shí)間和吞吐量袁勺,但這里有個(gè)大前提雹食,所有請(qǐng)求(如果做不到所有,至少也要絕大多數(shù)請(qǐng)求期丰,比如99.9%)都被成功處理了群叶,而不是返回一堆錯(cuò)誤碼。如果不能保證這一點(diǎn)钝荡,那么再低的響應(yīng)時(shí)間街立,再高的吞吐量都是沒有意義的。
誤區(qū)3:忘了測(cè)試端也存在性能瓶頸
性能測(cè)試的第三個(gè)誤區(qū)是只關(guān)注服務(wù)端埠通,而忽略了測(cè)試端本身可能也存在限制赎离。比如測(cè)試用例設(shè)置了10000并發(fā)數(shù),但實(shí)際運(yùn)行用例的機(jī)器最大只支持5000并發(fā)數(shù)端辱,如果只看服務(wù)端的數(shù)據(jù)梁剔,你可能會(huì)誤以為服務(wù)端最大就只支持5000并發(fā)數(shù)。如果遇到這種情況舞蔽,或者換用更高性能的測(cè)試機(jī)器荣病,或者增加測(cè)試機(jī)器的數(shù)量。
4 如何進(jìn)行性能測(cè)試渗柿?
介紹完性能測(cè)試相關(guān)的一些概念之后个盆,再來看一下有哪些工具可以進(jìn)行性能測(cè)試。
4.1 JMeter
JMeter可能是最常用的性能測(cè)試工具做祝。它既支持圖形界面,也支持命令行鸡岗,屬于黑盒測(cè)試的范疇混槐,對(duì)非開發(fā)人員比較友好,上手也非常容易轩性。圖形界面一般用于編寫声登、調(diào)試測(cè)試用例,而實(shí)際的性能測(cè)試建議還是在命令行下運(yùn)行揣苏。
并發(fā)設(shè)置
請(qǐng)求參數(shù)
結(jié)果報(bào)表
命令行下的常用命令:
- 設(shè)置JVM參數(shù):JVM_ARGS="-Xms2g -Xmx2g"
- 運(yùn)行測(cè)試:jmeter -n -t <jmx_file>
- 運(yùn)行測(cè)試同時(shí)生成報(bào)表:jmeter -n -t <jmx_file> -l <log_file> -e -o <report_dir>
除了JMeter悯嗓,其他常用的性能測(cè)試工具還有ab, http_load, wrk以及商用的LoaderRunner。
4.2 JMH
如果測(cè)試用例比較復(fù)雜卸察,或者負(fù)責(zé)性能測(cè)試的人員具有一定的開發(fā)能力脯厨,也可以考慮使用一些框架編寫單獨(dú)的性能測(cè)試程序。對(duì)于Java開發(fā)人員而言坑质,JMH是一個(gè)推薦的選擇合武。類似于JUnit临梗,JMH提供了一系列注解用于編寫測(cè)試用例,以及一個(gè)運(yùn)行測(cè)試的引擎稼跳。事實(shí)上盟庞,即將發(fā)布的JDK 9默認(rèn)就會(huì)包含JMH。
下面是我GitHub上的示例工程里的一個(gè)例子汤善,
@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中運(yùn)行
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(VacationClientBenchmark.class.getSimpleName())
.build();
new Runner(opt).run();
}
}
其中:
- @BenchmarkMode: 性能測(cè)試模式什猖,支持Throughput,AverageTime红淡,SingleShotTime等多種模式不狮。
- @Fork: 設(shè)置運(yùn)行性能測(cè)試的Fork進(jìn)程數(shù),默認(rèn)是0锉屈,表示共用JMH主進(jìn)程荤傲。
- @Threads: 并發(fā)數(shù),Threads.MAX表示同系統(tǒng)的CPU核數(shù)颈渊。
- @Warmup和@Measurement: 分別設(shè)置預(yù)熱和實(shí)際性能測(cè)試的運(yùn)行輪數(shù)遂黍,每輪持續(xù)的時(shí)間等
- @Setup和@Benchmark: 等同于JUnit里的@BeforeClass和@Test
在命令行下,使用JMH框架編寫的性能測(cè)試程序只能以Jar包的形式運(yùn)行(Main函數(shù)固定為org.openjdk.jmh.Main)俊嗽,因此一般會(huì)針對(duì)每個(gè)JMH程序單獨(dú)維護(hù)一個(gè)項(xiàng)目雾家。如果是Maven項(xiàng)目,可以使用官方提供的jmh-java-benchmark-archetype绍豁,如果是Gradle項(xiàng)目芯咧,可以使用jmh-gradle-plugin插件。
4 小結(jié)
以上就是我對(duì)性能測(cè)試的一些見解竹揍,歡迎你到我的留言板分享敬飒,和大家一起過過招。下一篇我將聊一下Web的自動(dòng)化測(cè)試芬位,敬請(qǐng)期待无拗。