本文首發于博客:https://zingphoy.github.io/2020/04/26/服務端壓測怎么做
博文的內容并不都是我原創的,行文思路來源于一次內部分享,再結合網上眾多參考資料總結出來的,算是一個學習筆記。
可能很多QA、RD同學跟我都一樣,對服務端壓測一直沒有系統的認知,印象停留在使用壓測工具如Jmeter對單接口發壓,調整線程數和循環數來制造不同壓力,最后計算一下TPS和成功率等就完事了?網上雖然有不少壓測相關的文章,但多數是壓測工具的入門級使用,有的是壓測流程和指標的簡單解釋,或者就是幾個大廠牛逼的全鏈路壓測能力和壓測平臺的介紹。這些文章要不缺少系統性闡述,要不過于抽象不好理解,對沒怎么接觸過壓測的同學不太友好。
本文嘗試在QA角度梳理一次完整的壓測過程,嘗試總結更為普適的壓測思路,給大家提供更有意義的參考。
壓測背景
測試分很多種,網上很多文章1會玩弄概念,搬出來3個名詞:壓力測試(Stress Testing)、性能測試(Performance Testing)、負載測試(Load Testing)。一般情況下并不需要做這么細粒度的概念區分,這3個概念我覺得是沒辦法完整區分各自邊界的,至少在程序邏輯上難以做得到,更多差異只是來自于不同的壓測策略,所以盡管忽略這幾個概念的區別,都叫它壓測或者性能測試即可。
為什么需要壓測
拿技術人熟知的阿里舉例,應該是國內做壓測最好的一個大廠。外界熟知的阿里2012雙11活動,2012年11月11日零點,阿里各種系統報錯、立刻下單報錯、購物車支付報錯、支付系統報錯、購物車的東西丟失,系統顯示交易成功率不到50%,產生了大量超賣,給阿里帶來很大的損失。那一年的雙11后,庫存、商品、退款和相應數據庫的同學,為了處理超賣導致的問題,沒日沒夜加了兩周的班,同時給了用戶不少糟糕購物體驗。
為什么出現這么嚴重的問題?因為對整個全交易鏈路上的各個子系統的承受能力不清楚,并且錯誤預估了可能會達到的流量,也沒有完善的預案,兵敗如山倒。
2013年阿里首次提出了全鏈路壓測方案:一方面可讓鏈路的各個系統知道自己的承壓極限;另一方面可讓各個系統有個明確的優化目標,了解到整個鏈路的瓶頸并評估資源情況。
單系統壓測與全鏈路壓測
為什么只做單系統壓測還不夠呢?
在活動開始的瞬間,各系統都面臨自身服務的巨大的壓力,而系統之間是有互相依賴關系的,單機壓測沒有考慮到依賴環節壓力都比較大的情況。一個系統出現故障,故障會在鏈路流轉過程中層層累加,會造成無法評估的影響。
所以最可靠的方式是完全模擬真實場景來壓測,通過線上全鏈路壓測提前發現問題。
壓測流程
完整的壓測流程一般包含下面幾個步驟,引用自文末參考資料:
- 壓測目標的制定
- 壓測鏈路的梳理
- 壓測環境的準備
- 壓測數據的構造
- 發壓測試
- 瓶頸定位及容量微調
- 壓測總結
壓測目標
壓測作用
- 新服務,無預估目標,需要通過壓測得到服務基準數據或找到系統瓶頸進行優化
- 有明確的壓測目標,需要通過壓測確定服務的各項指標是否達標
- 常態化壓測,為后期性能優化指導方向或提供參考依據
壓測指標
列舉一些常用指標,并不一定都需要關注,根據業務考慮指標的細化粒度。
- QPS:Query Per Second,每秒處理的請求個數
- TPS:Transactions Per Second,每秒處理的事務數,TPS <= QPS
- RT: Response Time,響應時間,等價于Latency RT分平均延時,Pct延時(Percentile分位數)。平均值不能反映服務真實響應延時,實際壓測中一般參考Pct90,Pct99等指標
- CPU使用率:出于節點宕機后負載均衡的考慮,一般 CPU使用率 < 75% 比較合適
- 內存使用率:內存占用情況,一般觀察內存是否有尖刺或泄露
- Load指標:CPU的負載,不是指CPU的使用率,而是在一段時間內CPU正在處理以及等待CPU處理的進程數之和的統計信息,表示CPU的負載情況,一般情況下 Load < CPU的核數*2,更多參考鏈接1和鏈接2
- 緩存命中率:多少流量能命中緩存層(redis、memcached等)
- 數據庫耗時:數據庫就是業務的生命,很多時候業務崩掉是因為數據庫掛了
- 網絡帶寬:帶寬是否瓶頸
- 接口響應錯誤率 or 錯誤日志量
這里要說明一下QPS和TPS的區別:
- QPS一般是指一臺服務器每秒能夠響應的查詢次數,或者抽象理解成每秒能應對多少網絡流量
- TPS是指一個完整事務,一個事務可能包含一系列的請求過程。舉個,訪問一個網頁,這是一個TPS,但是訪問一個網頁可能會對多個服務器發起多次請求,包括文本、js、圖片等,這些請求會當做多次QPS計算在內,因為它們都是流量
性能測試中,平均值的作用是十分有限的,平均值代表前后各有50%的量,對于一個敏感的性能指標,我們取平均值到底意味著什么?是讓50%的用戶對響應時間hAppy,但是50%的用戶感知到響應延遲?還是說50%的時間系統能保證穩定,而50%的時間系統則是一個不可控狀態?
平均響應時間這種指標,只有在你每次請求的響應時間都是幾乎一樣的前提下才會有一樣。再來個例子,人均財富這個概念有多沙雕相信大家也明白,19年有個很搞笑的新聞——騰訊員工平均月薪七萬,明白平均值多不靠譜了吧。下圖是現實世界中一個系統的響應時間柱狀圖,RT在前20%的請求數較少,但是因為耗時特別短(拉高了均值可能是命中緩存,也可能是請求的快速失敗),而大多數RT是在均值之下,那才是系統的實際性能情況。
所以說,我們不應看最好的結果,相反地,應該控制最壞的結果,用戶用得爽他不保證會傳播好口碑,但是用戶用到生氣他保證變為鍵盤俠肆意大罵,這也是為什么平均值無法帶來足夠的參考,因為happy的結果蒙蔽了我們的雙眼,平均值在壓測中丟失太多信息量。
總結一下,較為科學的評估方法應該將指標-成功率-流量三者掛鉤在一起的:
xx%的響應在xx毫秒內返回,其中成功率為xx%。
根據這個方針,可以得到一些測試思路:
- 在響應時間的限制下,系統最高的吞吐量(這里不對吞吐量做嚴格定義,當成是QPS或TPS即可)
- 在成功率100%的前提下,不考慮響應時間長短,系統能承受的吞吐量
- 容忍一定的失敗率和慢響應,系統最高能承受的吞吐量(95%成功率,前95%的請求響應時間為xx毫秒時的最大QPS)
- 在上面的場景下還要考慮時間和資源,比如最高吞吐量持續10分鐘和持續1小時是不一樣的,不同的時間持續長度下,機器資源(cpu、內存、負載、句柄、線程數、IO、帶寬)的占用是否合理
目標預估
壓測開展前是需要有目標的,也就是有期望的性能情況,希望接口或系統能達到的性能預期,沒有目的的壓測是浪費人力,下面給出幾種目標預估的方法。
歷史監控數據
已經上線并且有歷史監控數據的接口,可以查看歷史數據,找出峰值QPS和PCT99。 若接口A已經上線并且做了監控,在經過某次大活動或者上線時間足夠長后,存量監控數據就可以使用了。
類比
新接口或者線上未監控的接口,不存在歷史數據,但存在類似功能接口的歷史監控數據,可以通過類比得出壓測的目標。 假設上一年淘寶雙十一下訂單接口QPS=x,RT=y,這一年天貓平臺整起來了,雙十一活動與上年淘寶雙十一活動場景類似,也沿用QPS=x,RT=y的目標(例子不嚴謹,理解即可)。
估算
新接口或者線上未監控的接口,不存在歷史數據,且不存在類似功能接口的數據可供參數考,此時需要估算峰值,常用方法有8/2原則——一天內80%的請求會在20%的時間內到達。
top QPS = (總PV * 0.8) / (60 * 60 * 24 * 0.2)
RT如無特殊要求,一般采用默認值:
- 單服務單表類,RT<100ms
- 較復雜接口,RT<300ms
- 大數據量或調用鏈較長的接口,RT<1s
-1 電商秒殺活動,預估同時有1000w人參與,簡單起見假設總QPS是1000w。由于前端不同的秒殺倒計時形式使得請求有2s的打散,再加上Nginx等webserver做了20%幾率拒絕請求的策略,所以下單接口總QPS = 1000w / 2 * (1 - 0.2) = 400w/s,最終壓測目標為400w/s的QPS。
-2 電商全天低價搶購活動,屠龍寶刀,點擊就送,一刀99級,emmmmm跑題了。根據8/2原則,預估在午休(12-1)和晚上下班后(7-10)共4h是流量高峰,估算接口峰值QPS = 活動全天接口PV / (4*3600s)。
其他
除了前面說到的情況,肯定還有一些我們無法下手的三無接口,無參考、無預估、無歷史數據,這時候只能一點一點來,慢慢把壓力提上去的同時收集數據,最終得出接口的最優處理能力。
壓測準備
壓測場景
壓測是有目的的壓測,也就是說不是隨便找些接口發一通壓力,而壓測全部的接口也是做不到的或者說無意義的,得有壓測的優先級,所以梳理壓測場景是很重要的。高優場景主要有下面幾個:
- 高頻業務場景(今日頭條首頁下拉刷新)
- 關鍵業務場景,使用頻率低,一旦出問題就很嚴重(微信賬號登錄)
- 性能高消耗場景(淘寶下單)
- 曾經出現過問題的場景
壓測有分單接口壓測和場景化壓測,前者會簡單一些,后者一般是多個接口混合操作以組成一個業務場景,兩者在方法上是相通的。
梳理場景時QA需要與RD對齊,確認不同接口的RD負責人、需要壓測的接口、系統性能現狀以及壓測目標;在確定每個接口的壓測目標時,要考慮到壓測對象是單實例單機房還是集群;在細節上也要確認是單接口壓測還是場景化壓測,每個接口的流量占比以及優先級,需不需要發足夠的壓力來觸發系統的自動擴容或降級等更進一步的運維能力。
壓測環境
在梳理完壓測場景后,就要確認壓測鏈路是否完整或符合預期。從一個服務到另一個服務,是不是鏈路上每一個服務都要壓到?下游服務如審計安全等是不是已經考慮到?壓測過程中產生的臟數據是否會影響線上數據?可能還要細化到具體下游某個服務不參與壓測,如何處理呢?以上種種問題,可能需要推動整個鏈路相關的業務方進行對應業務改造來適配壓測流量,改造完后還要自測驗證才能正式開始壓測,下面講一些重點問題,部分內容引用自文末的參考資料。
臟數據問題
- 如果是在獨立的一套環境中操作,不存在該問題
- 影子表:如果是在線上操作,一般將數據寫入影子表(與原數據表在schema上一致的不同名表)而非原數據表,實現壓測數據與線上數據隔離
- 白名單:指定測試id或者測試賬號,在入庫后通過統一id區分壓測數據,統一處理
- 各類存儲層的壓測改造,包括緩存層、消息隊列、離線數據庫等隔離問題。常規方法在壓測鏈路中透傳壓測標記(也叫流量染色,挺形象的),比如json數據中加is_stress標記,存儲層根據標記區分壓測流量,對壓測數據添加指定前后綴再入庫等特殊處理
不參與壓測的服務如何處理
- mock server:通過錄制request和response的方式,對業務的代碼實現無侵入
- 服務stub:針對壓測流量做處理,類似單測stub,代碼stub模擬服務返回response,需要修改代碼
可以獨立部署一套線下環境進行壓測。在不影響線上環境的前提下,確保機房,網絡,存儲,上下游服務與線上保持一致,部署一套獨立的環境進行測試,機器與線上隔離,機器出問題不會影響線上。這種方式壓測只是針對較少的幾個系統進行,因為很難把整個鏈路所有系統都獨立再部署一套,所以應用范圍有限。
備注:上游、下游如何定義? RFC 2616
Upstream and downstream describe the flow of a message: all messages flow from upstream to downstream.
下游的輸入來自于上游的輸出,假設有服務A、B,A調用了B(或者說A依賴B),那B就是A的上游,A就是B的下游,因為A的輸入來自于B的輸出(A調用了B,獲取B的輸出)。
更簡單地理解,越接近用戶的東西,越是下游。
更常見的方法是直接使用線上環境壓測,在機器負載低的時間段(如深夜)人工發起或定時發起壓測。
壓測監控體系
確認好壓測流程的技術支持和Mock數據的支持后,還要確認壓測鏈路的監控體系是否完整,一來方便在壓測過程中及時發現問題,二來是為了積攢歷史壓測數據,三來順便確認監控系統本身是否可靠且全部到位。一般監控項包括(也就是壓測指標):
- 核心接口和核心依賴的流量、響應耗時、成功率
- 消息隊列、緩存、數據庫
- 機器物理資源
壓測數據
壓測數據其實沒什么神秘的,網上說什么按照業務模型產出數據,表達上做了過度抽象反而不好理解,其實意思就是按照業務核心場景將所需要的數據構造出來。關鍵是要如何科學地模擬線上數據分布,引用文末參考資料,阿里雙十一大促中有如下的業務流量漏斗模型,需要給不同場景科學地分配流量比例,這個比例是分析出來的而不是拍腦袋的。可以想象,阿里大促的流量不可能全部最后都走到付款流程,必然很多流量會在前面的流程就結束了,也就意味著,你把全部壓測數據都構造成【走到付款場景】的話,你的壓測結果是不準確的。
為了更好模擬線上真實的用戶使用場景和數據,dump線上數據用來壓測是很常見的手段,有兩種簡單思路:
- 直接回放提前錄制的線上流量到壓測鏈路
- 將現成流量copy一部分引流到壓測鏈路
數據dump下來是不能直接用的,一來沒加壓測標記會污染線上數據,二來涉及用戶隱私數據。可以將線上數據作為數據源,經過采集、過濾、脫敏等操作后轉變為壓測數據,注意點有:
- 確保數據已添加壓測標記
- 賬戶數據要提前完成登錄認證等準備工作
- 數據要盡可能跟真實數據保持一致,比如,價格,圖片等
- 數據是否有不同設備型號等特殊要求
- 盡量保持和線上相同緩存的命中率
- 其他業務特性上的特殊要求……
壓測過程
基本思路跟做質量保障是一樣的,從細粒度開始慢慢集成到整個大系統,就像單測->接口測試->集成測試,壓測也是先從簡單的開始,一步一步走向全資源全鏈路,可以參考過程:單接口單機->單接口1/4資源->場景化1/4資源->全量資源壓測->撥測。
單接口單機
在單核(或物理資源少)機器上部署單個服務,排除外部鏈路、網絡等因素,得出自身服務的單核性能情況(單位QPS/core),后續根據此單核性能指標結合壓測目標值進行擴容。另外由于是壓的單接口單機,無其他接口請求影響,上下游在足夠資源的情況下也不會造成瓶頸,所以能確保服務的性能真實值。
單接口單機可以在正式開始大規模壓測前提前發現問題,方便RD做針對的性能優化并快速檢驗優化效果。一部分問題會先在單接口單機壓測環節中發現,而一些隱藏得更深的問題,需要延后到全鏈路大流量壓測才能暴露。
單接口1/4資源
單接口單機壓測環節,服務端已經完成了部分性能優化,接下來可以進入單接口1/4資源壓測,這樣是為了驗證在單接口單機壓測中得到的單核性能數據,在擴容1/4資源下性能是否會線性增長,是否存在性能損耗以及定位損耗源。
場景化1/4資源
單接口壓測局限很明顯,場景化壓測由于引入了上下游服務的其他接口的因素,可以發現單接口壓測無法發現的問題,更接近線上用戶場景。
全量資源全鏈路
全部資源到位后,預估的線上壓力是否能承受,這一步也是內網壓測過程的最后一步。
撥測
除了做內網壓測,還要進行撥測驗證用戶從客戶端到服務端的整個帶寬資源是否滿足預期,內網壓測已經確認了業務性能是否達標,因此撥測可以只選擇了一個場景進行驗證即可。(簡單來說撥測相當于壓測cdn,檢查各地cdn節點資源是否充足)
壓測策略
壓測過程也要提前規劃好,然后加入一定的人工策略調整。阿里大促還會有預熱環節,預先跑一部分流量使得該緩存的數據提前緩存起來。正式壓測時細分有幾種壓測策略,引用自文末參考資料:
- 峰值脈沖:流量是逐漸變大的一個小坡,還是驟升后保持高峰
- 系統摸高:關閉熔斷降級限流等fallback功能,提高壓力觀察系統性能轉折點
- fallback策略驗證:開啟熔斷限流等fallback功能,這些功能是否生效,系統是否還能扛得住
- 破壞性測試:主要為了驗證預案的有效性,類似于容災演練時的預案執行演練,驗證后手搶救方案
除了關注前面講到的指標外,還需要關注各機房流量是否均勻(若不均勻要確認負載均衡是否work)。
壓測收尾
發壓環節的結束并不代表壓測就到此為止。
數據清理
如果使用了影子表,可能收尾工作會簡單一些,只需要下掉影子表即可。如果數據直接落到了線上數據庫,可能一大堆壓測數據要清理,壓測時會對數據染色(比如指定測試賬號或流量攜帶壓測標記),逐層透傳,最后根據標志識別刪除。
常見問題
舉例一些可能會發現的典型問題:
- 存在多余的http header,導致額外帶寬占用
- spin_lock對RT影響大,優化鎖的方式
- 調整nginx worker數量可提高性能
- 不恰當的長鏈接數
- 代碼實現上對象沒有較好復用
- cache命中率不符預期
- 業務流程上存在冗余
- 缺少一層cache
- 響應碼or錯誤碼可能要繼續規范
- 下游服務資源不足(其他監控、存儲)
- 內部系統對壓測的限流,需要變更配置或者協商解除限制
……
壓測總結
給出一個完整的壓測過程例子:
- 確定本次的壓測目標,預估各項指標的達標值
- 根據服務接口的優先級和使用場景,確認出需要壓測的接口
- 梳理壓測鏈路上的服務,確認鏈路完整性
- 針對壓測鏈路設計的服務進行壓測改造
- 準備壓測數據,確認壓測策略
- 開始壓測,監控各項指標,多輪壓測檢驗性能優化效果
- 壓測環境清理
- 壓測總結報告輸出
壓測最終應該輸出一份報告總結,其實也就是把整個壓測方案、過程、結論記錄下來,寫明壓測目標、壓測接口、壓測數據、壓測結論,給出發現的問題并提供優化方案。往往在壓測報告完成時,性能問題已經基本被解決了,報告的意義在于梳理前面的整個流程,給后續的壓測提供經驗指導。
參考資料
[1] https://www.guru99.com/performance-vs-load-vs-stress-testing.html
[2] https://www.toolsou.com/en/article/200293789
Why Averages Suck and Percentiles are Great
CoolShell-性能測試怎么做
全鏈路壓測的大概思路
獨家揭秘 | 阿里怎么做雙11全鏈路壓測?
親歷流量尖峰時刻:一名阿里技術員工的雙11十年
What is Upstream and Downstream in Software Development?
一個阿里技術男經歷的六年“雙11”:技術改變阿里