聲明
本文由Donny譯自 3scale.com 的 《How to load test & tune performance on your API》
前言
這幾年API的作用不斷演化,以前API還只是用來做內(nèi)部系統(tǒng)之間的集成點(diǎn),但現(xiàn)在API已成為一個(gè)公司的核心系統(tǒng),一個(gè)構(gòu)建于Web和移動(dòng)端應(yīng)用之上的核心系統(tǒng)。
當(dāng)API僅只用來處理后臺(tái)的任務(wù)(例如生成報(bào)告),那么性能差點(diǎn)也不是問題。但是如今API慢慢地發(fā)展成為連接服務(wù)與終端用戶的核心紐帶。這種關(guān)鍵性的角色變化表明了一個(gè)重要的觀點(diǎn):那就是API的性能真的很重要。
如果API數(shù)據(jù)源響應(yīng)快,前端的應(yīng)用程序的設(shè)計(jì)好點(diǎn)或差點(diǎn)影響不大,要是響應(yīng)慢如蝸牛,前端的設(shè)計(jì)再出色也是然并卵?,F(xiàn)在我們的客戶端應(yīng)用展示的數(shù)據(jù)源可能都是來自多個(gè)API響應(yīng)內(nèi)容的聚合,性能對(duì)這種微服務(wù)構(gòu)架來說真的非常重要。
可以毫不夸張的說出色的性能就是你API提供的最好功能。我們知道向目標(biāo)改進(jìn)的唯一正確的方法就是找到問題的關(guān)鍵點(diǎn),或者叫關(guān)鍵路徑,并不斷迭代測(cè)量和調(diào)整你的架構(gòu)系統(tǒng),直到系統(tǒng)達(dá)到預(yù)定的目標(biāo)。對(duì)于API來說,測(cè)量和提高性能的過程就是負(fù)載與壓力測(cè)試的過程。
本文將重點(diǎn)介紹如何對(duì)你的API進(jìn)行負(fù)載壓力測(cè)試。我們會(huì)以一個(gè)簡(jiǎn)單的、未測(cè)過的例子開始,然后再添加一個(gè)訪問控制層,要確保一切都經(jīng)過嚴(yán)格測(cè)試,做好處理真實(shí)流量的準(zhǔn)備工作。OK,開始吧!
準(zhǔn)備工作
首先我們要明確要測(cè)試什么,可以是對(duì)你所有的API接口,或者是對(duì)單個(gè)API接口,或是對(duì)需要排除故障或改進(jìn)的API接口的常規(guī)測(cè)試。
本文的其部分,我們將使用一個(gè)示例API。這是一個(gè)棋牌類游戲的Node.js API。它有三個(gè)API接口:
/question – 返回一個(gè)隨機(jī)黑牌
/answer – 返回一個(gè)隨機(jī)白牌
/pick – 返回一對(duì)隨機(jī)的問題與答案
你測(cè)試用的負(fù)荷情況越和真實(shí)環(huán)境的越類似,你的負(fù)載測(cè)試就越有用。如果你不知道實(shí)際流量有多少或者你不知道負(fù)載在所有接口上是否都一致,那么就算你知道你的API可以保持400 請(qǐng)求/秒的吞吐量也沒啥鳥用。
所以,你應(yīng)該先從收集你API的使用數(shù)據(jù)開始。你可以直接從你的API服務(wù)日志或者從其他你在用的應(yīng)用性能工具(例如New Relic)中獲取數(shù)據(jù)。在對(duì)你的API進(jìn)行第一次測(cè)試之前,你應(yīng)該對(duì)以下問題做到心中有數(shù):
(1)每秒請(qǐng)求數(shù)的平均吞吐量(Average throughput in requests per second)
(2)峰值吞吐量(您在某段時(shí)間內(nèi)獲得的最大流量是多少?)(Peak throughput)
(3)API各接口的吞吐量分布情況(有沒有一些接口的流量遠(yuǎn)超其他接口?)
(4)用戶的吞吐量分布情況(少數(shù)用戶產(chǎn)生大多數(shù)的流量,或者是更均勻分布?)
另外還需要考慮的一個(gè)關(guān)鍵點(diǎn)是,在測(cè)試期間將要模擬的流量會(huì)是怎樣的,主要考慮點(diǎn)是:
(1)重復(fù)負(fù)載生成(Repetitive load generation)
(2)模擬流量模式
(3)真實(shí)流量
通常我們最好以最簡(jiǎn)單的方法開始測(cè)試,然后逐步演化到更為接近真實(shí)環(huán)境的測(cè)試。我們可以先用重復(fù)負(fù)載生成來做為API接口的第一個(gè)測(cè)試,這樣不僅可以驗(yàn)證我們的測(cè)試環(huán)境是否穩(wěn)定,更重要的是可以讓我們找到API能承受的最大吞吐量,這樣我們就可以知道API可以達(dá)到的性能上限是多少。
找到你的API性能上限值后,你就可以開始考慮如何將你的生成的測(cè)試流量塑造得更接近真實(shí)環(huán)境。使用真實(shí)流量來測(cè)試是最理想的,但實(shí)際操作不太可行。要模擬真實(shí)流量比較難,也太花時(shí)間。所以我們有一個(gè)折中點(diǎn)的方法:先研究你的流量分析數(shù)據(jù),并做一個(gè)簡(jiǎn)單的概率模擬。比如你有100個(gè)API接口(提示:原文endpoint在這里我譯為接口,翻譯成端點(diǎn)也可以,不過譯成接口感覺更容易理解),你檢查了上個(gè)月的使用情況,發(fā)現(xiàn)80%的流量來自20個(gè)接口,其中3個(gè)接口占用了50%的流量。那么你就可以創(chuàng)建一個(gè)遵循這種概率的請(qǐng)求列表,并提供給你的負(fù)載測(cè)試工具。這樣做就相對(duì)快多了,并且它相對(duì)比較接近你真實(shí)負(fù)載,可以顯示出你實(shí)際環(huán)境中可能遇到的問題。
最后,如果你拿到你要測(cè)試的API的真實(shí)訪問日志,你就可以用它們來做最接近客觀現(xiàn)實(shí)的測(cè)試。我們待會(huì)兒要討論的大部分負(fù)載測(cè)試工具,都是接收一個(gè)請(qǐng)求列表作為輸入文件。你可以用你的訪問日志,稍微做一個(gè)格式調(diào)整就可以匹配每個(gè)測(cè)試工具所需的格式。搞定這個(gè)你就可以在測(cè)試環(huán)境中輕松重現(xiàn)你的生產(chǎn)流量。
配置你的負(fù)載測(cè)試環(huán)境
好了,你清楚了你要測(cè)試什么鬼了,準(zhǔn)備工作的最后一步就是配置好你的測(cè)試環(huán)境。你需要一個(gè)專用的測(cè)試環(huán)境。如果你不怕被你老板罵的話,或者比較任性,你也可以直接在你的生產(chǎn)環(huán)境中進(jìn)行性能測(cè)試,不過出問題別說哥事先沒跟你說清楚哈。
如果您已經(jīng)設(shè)好一個(gè)預(yù)生產(chǎn)或沙箱環(huán)境,并且你的API也在上面運(yùn)行了,那么你就萬事俱備了。因?yàn)楸疚囊檬纠鼳PI,我們會(huì)在AWS的服務(wù)實(shí)例上設(shè)置我們的環(huán)境。
在我們的例子中,我們使用一個(gè)簡(jiǎn)單的API,不需要從磁盤讀取或在內(nèi)存中保存大型數(shù)據(jù)集。我們選擇Linux C4.large實(shí)例就夠了。
注意:我們對(duì)比過其他相似處理資源數(shù)但內(nèi)存更大的AWS實(shí)例,但實(shí)際測(cè)試中內(nèi)存大部分沒使用,所以我們選了C4.large
接下來,我們將一個(gè)配好的負(fù)載測(cè)試實(shí)例(服務(wù)器)運(yùn)行起來,這只是一個(gè)運(yùn)行模擬測(cè)試程序的服務(wù)器,它會(huì)通過從多個(gè)并發(fā)連接重復(fù)發(fā)送請(qǐng)求到我們的API服務(wù)器。你需要模擬的負(fù)載越高,機(jī)器的性能就要求越高。再次,這也是一個(gè)CPU密集型工作負(fù)載。這里我們選擇具有4個(gè)虛擬核,16個(gè) ECU的優(yōu)化處理器的c4.xlarge AWS服務(wù)器
我們選擇在相同的可用區(qū)內(nèi)部署所有實(shí)例(API服務(wù)器與測(cè)試服務(wù)器在同一個(gè)區(qū)/機(jī)房),這樣可以將外部因素對(duì)我們測(cè)試結(jié)果的影響降到最小。
選擇測(cè)試工具
我們有一個(gè)沙箱環(huán)境來運(yùn)行我們的API,同時(shí)也有另一臺(tái)服務(wù)器準(zhǔn)備開始負(fù)載測(cè)試。如果這是你第一次做性能測(cè)試,你一定會(huì)想知道什么是最好的方法。在本節(jié)中,我們將會(huì)分享我們?nèi)绾芜x擇工具,同時(shí)也會(huì)介紹一下目前市面上一些公認(rèn)比較好的工具。
JMeter
在人們意識(shí)當(dāng)中,首當(dāng)翹楚的估計(jì)是Apache JMeter,這是一個(gè)開源的Java程序,他關(guān)鍵的特性就是提供一個(gè)強(qiáng)大而完善的創(chuàng)建測(cè)試計(jì)劃的GUI。測(cè)試計(jì)劃由測(cè)試組件組成,測(cè)試組件定義了測(cè)試的每一個(gè)部分,例如:
(1)用來注入負(fù)載測(cè)試的線程
(2)參數(shù)化測(cè)試中使用的HTTP請(qǐng)求
(3)可添加偵聽器,象widget測(cè)試組件那樣,可以以不同的方式顯示測(cè)主式結(jié)果
優(yōu)點(diǎn):
(1)它是功能性負(fù)載測(cè)試的最好工具。你可以設(shè)定條件來為復(fù)雜的用戶流建模,還可以創(chuàng)建斷言來驗(yàn)證行為。
(2)輕松模擬復(fù)雜的http請(qǐng)求,比如請(qǐng)求前的登錄驗(yàn)證或文件上傳
(3)可擴(kuò)展性強(qiáng),有很多社區(qū)插件可以修改或擴(kuò)展內(nèi)置的行為
(4)開源并且免費(fèi)
缺點(diǎn):
(1)GUI學(xué)習(xí)曲線陡峭,一大堆的選項(xiàng),在你運(yùn)行第一個(gè)測(cè)試之前你得了解大量的概念。
(2)測(cè)試高負(fù)載時(shí),操作步驟很麻煩。你需要先使用GUI工具來生成XML測(cè)試計(jì)劃,然后在非GUI模式下導(dǎo)入測(cè)試計(jì)劃運(yùn)行測(cè)試,因?yàn)镚UI會(huì)消耗掉本用于生成負(fù)載的大量資源。你還需要注意所有的偵聽器(收集數(shù)據(jù)與展示測(cè)量的組件)哪些要被禁用或啟用,因?yàn)樗鼈円埠芎馁Y源。測(cè)試結(jié)束后后,你需要將原始結(jié)果數(shù)據(jù)導(dǎo)入GUI以才能查看結(jié)果。
(3)如果你的目標(biāo)是測(cè)試一段時(shí)間內(nèi)的持續(xù)吞吐量(例如在60秒內(nèi)每秒請(qǐng)求1000次),那么很難找到正確的并發(fā)線程數(shù)量和計(jì)時(shí)器來求出一個(gè)比較穩(wěn)定的數(shù)值。
JMeter只是我們?cè)陂_始測(cè)試時(shí)用的工具,我們很快開始尋找其他替代方案。原因是,如果你的目標(biāo)是在Web應(yīng)用上壓力測(cè)試復(fù)雜的用戶流,那么JMeter可能是最好的工具,但如果你只是需要在一些HTTP API接口上進(jìn)行性能測(cè)試,那用它就是殺雞用牛刀了。
Wrk
Wrk是一款和傳統(tǒng)的Apache Benchmark(最初用來做Apache服務(wù)器的測(cè)試工具)非常相似的工具。wrk和ab完全不同于JMeter:
(1)一切都是可以通過命令行工具配置和執(zhí)行的。
(2)配置少但強(qiáng)大,只有基本生成HTTP負(fù)載的必要幾項(xiàng)配置
(3)性能強(qiáng)悍
然而,和傳統(tǒng)ab工具相比還是有幾個(gè)優(yōu)勢(shì)的地方,主要是:
(1)多線程,所以能利用多核處理器的優(yōu)勢(shì),更容易生成更高的負(fù)載
(2)利用Lua腳本很容易進(jìn)行擴(kuò)展默認(rèn)的行為
不好的地方,主要是生成的默認(rèn)報(bào)告在內(nèi)容與格式上都受到限制(僅文本,無繪圖)。當(dāng)你的目標(biāo)是找到你的API可以處理的最大負(fù)載量,那么wrk是你最佳選擇工具。wrk用起來很快就可以上手。
Vegeta
Vegeta是一款開源命令行工具,但它采用的方式不同于我們以前所見的工具。它專注于如何達(dá)到與維持每秒請(qǐng)求數(shù)速率。也就是說它側(cè)重在測(cè)試支撐每秒X次請(qǐng)求時(shí)API會(huì)有怎樣的服務(wù)行為,當(dāng)你有實(shí)際的數(shù)據(jù)或?qū)δ銓⒁_(dá)到的峰值流量有個(gè)估算時(shí)就非常有用,你可以用于驗(yàn)證你的API是否能滿足你的需求。
SaaS 工具
正如你之前所看到的,運(yùn)行一個(gè)簡(jiǎn)單的負(fù)載測(cè)試需要準(zhǔn)備好配置環(huán)境。最近有些產(chǎn)品提供負(fù)載測(cè)試服務(wù)。我們?cè)囘^兩個(gè),Loader.io和Blazemeter(話外:阿里也有性能測(cè)試工具PTS,老外估計(jì)沒試過)。
注意:我們只試了這兩個(gè)工具的免費(fèi)版,所以得到的測(cè)試結(jié)果僅適用于免費(fèi)版的限定。
Blazemeter
這個(gè)產(chǎn)品和我們前面提到的JMeter一樣有同樣的毛?。喝绻阒恍枰迷诟哓?fù)載測(cè)試,你需要在GUI界面上創(chuàng)建測(cè)試計(jì)劃,然后在另一個(gè)運(yùn)行非GUI模式的JMeter中導(dǎo)入這些計(jì)劃。Blazemeter允許你上傳JMeter的測(cè)試計(jì)劃到他們的云端并運(yùn)行,但可惜的是免費(fèi)版只能設(shè)置50個(gè)并發(fā)用戶。
Loader.io
它是一款SendGrid出品的簡(jiǎn)單而強(qiáng)大的云負(fù)載測(cè)試服務(wù)工具。它有你所需要的功能和漂亮的可視報(bào)告。 Loader.io 的免費(fèi)版還是不錯(cuò)的,每秒最多可以有10000次請(qǐng)求的吞吐量,你基本上就可以用它來運(yùn)行一個(gè)真實(shí)的負(fù)載測(cè)試。
我們推薦使用多個(gè)工具,以便可以多重檢查我們的測(cè)試結(jié)果,不同的工具有不同的功能與方法,可以更多方面地反映測(cè)試結(jié)果。
建立測(cè)試基線
我們先嘗試找到我們的API可以承受的最大吞吐量。在這個(gè)吞吐量下,我們的API服務(wù)達(dá)到最大CPU利用率,同時(shí)不會(huì)返回任何錯(cuò)誤或超時(shí)。這個(gè)吞吐量就可作為我們后面測(cè)試要用的每秒請(qǐng)求數(shù)。
同樣,重要的是要注意到:CPU是限制因素之一,但你也還必須清楚地知道哪些資源會(huì)成為你API的性能瓶頸。
我們有必要在API服務(wù)器上安裝一些工具,以便我們?cè)跍y(cè)試過程中監(jiān)控資源的利用率情況。我們使用?Keymetrics.io?和?PM2?模塊。
我們的Node.js應(yīng)用運(yùn)行了一個(gè)非常簡(jiǎn)單的HTTP 服務(wù)。Node.js是單線程設(shè)計(jì)的,但為了利用c4.large AWS實(shí)例中提供的雙核,我們使用PM2的集群功能來運(yùn)行應(yīng)用程序的兩個(gè)工作進(jìn)程。
由于我們的API是完全無狀態(tài)的,所以很容易使用PM2的 核心集群模塊(PM2在內(nèi)部直接使用)。PM2提供的集群功能提供了不錯(cuò)的快捷命令來start/stop/reload應(yīng)用程序,也可以監(jiān)控進(jìn)程。
首次運(yùn)行
我們先使用Loader.io對(duì)API進(jìn)行測(cè)試。以下是持續(xù)30秒,每秒10,000次請(qǐng)求的測(cè)試結(jié)果,10000次請(qǐng)求是Loader.io免費(fèi)版中允許的最大吞吐量。
在測(cè)試期間,我們觀察到API服務(wù)器的CPU處理器在測(cè)試期間只有幾次達(dá)到100%的容量。
這表示我們的API可能還可以處理更高的吞吐量。我們接下來通過運(yùn)行wrk進(jìn)行第二次測(cè)試證實(shí)了這一點(diǎn)。我們的目標(biāo)就是要將我們的API服務(wù)器性能推到極限。
wrk -t 4 -c 1000 -d 60 --latency --timeout 3s http://api-server/questions
這里是我們對(duì)這個(gè)測(cè)試做了多次重復(fù)測(cè)試的結(jié)果:
Running 1m test @ http://api-server/question
4 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 62.23ms 30.85ms 1.35s 99.39%
Req/Sec 4.07k 357.61 5.27k 94.29%
Latency Distribution
50% 60.04ms
75% 63.85ms
90% 64.17ms
99% 75.86ms
972482 requests in 1.00m, 189.89MB read
Requests/sec: 16206.04
Transfer/sec: 3.16MB
結(jié)果表明,我們的預(yù)感被證實(shí):它達(dá)到16,206請(qǐng)求/秒,同時(shí)保持合理的延遲,第99百分位只有75.86毫秒。 我們將這作為我們的基準(zhǔn)最大吞吐量,因?yàn)檫@一次我們看到了API服務(wù)器的最大容量處理能力:
接下來
我們剛看到用一個(gè)簡(jiǎn)單的方式來找出你的API可承受的最大流量負(fù)載,同時(shí)在這過程中我們介紹并討論了我們看到的一些工具。
請(qǐng)繼續(xù)關(guān)注本文的第二部分,我們將介紹如何控制流量,不要讓隨隨便便一個(gè)客戶端就可以輕松搞跨您的API。 我們將展示如何通過在架構(gòu)前端添加代理來確保我們的API的性能不受影響。