全链路压测平台(Quake)在美团中的实践
背景
在美團(tuán)的價(jià)值觀中,“以客戶為中心”被放在一個(gè)非常重要的位置,所以我們對(duì)服務(wù)出現(xiàn)故障越來越不能容忍。特別是目前公司業(yè)務(wù)正在高速增長(zhǎng)階段,每一次故障對(duì)公司來說都是一筆非常不小的損失。而整個(gè)IT基礎(chǔ)設(shè)施非常復(fù)雜,包括網(wǎng)絡(luò)、服務(wù)器、操作系統(tǒng)以及應(yīng)用層面都可能出現(xiàn)問題。在這種背景下,我們必須對(duì)服務(wù)進(jìn)行一次全方位的“體檢”,從而來保障美團(tuán)多個(gè)業(yè)務(wù)服務(wù)的穩(wěn)定性,提供優(yōu)質(zhì)的用戶服務(wù)體驗(yàn)。真正通過以下技術(shù)手段,來幫助大家吃的更好,生活更好:
- 驗(yàn)證峰值流量下服務(wù)的穩(wěn)定性和伸縮性。
- 驗(yàn)證新上線功能的穩(wěn)定性。
- 進(jìn)行降級(jí)、報(bào)警等故障演練。
- 對(duì)線上服務(wù)進(jìn)行更準(zhǔn)確的容量評(píng)估。
- ……
全鏈路壓測(cè)是基于線上真實(shí)環(huán)境和實(shí)際業(yè)務(wù)場(chǎng)景,通過模擬海量的用戶請(qǐng)求,來對(duì)整個(gè)系統(tǒng)進(jìn)行壓力測(cè)試。早期,我們?cè)跊]有全鏈路壓測(cè)的情況下,主要的壓測(cè)方式有:
- 對(duì)線上的單機(jī)或集群發(fā)起服務(wù)調(diào)用。
- 將線上流量進(jìn)行錄制,然后在單臺(tái)機(jī)器上進(jìn)行回放。
- 通過修改權(quán)重的方式進(jìn)行引流壓測(cè)。
但以上方式很難全面的對(duì)整個(gè)服務(wù)集群進(jìn)行壓測(cè),如果以局部結(jié)果推算整個(gè)集群的健康狀況,往往會(huì)“以偏概全”,無法評(píng)估整個(gè)系統(tǒng)的真實(shí)性能水平,主要的原因包括:
- 只關(guān)注涉及的核心服務(wù),無法覆蓋到所有的環(huán)節(jié)。
- 系統(tǒng)之間都是通過一些基礎(chǔ)服務(wù)進(jìn)行串聯(lián),如 Nginx、Redis 緩存、數(shù)據(jù)庫、磁盤、網(wǎng)絡(luò)等等,而基礎(chǔ)服務(wù)問題在單服務(wù)壓測(cè)中往往不能被暴露出來。
綜合多種因素考慮,全鏈路壓測(cè)是我們準(zhǔn)確評(píng)估整個(gè)系統(tǒng)性能水平的必經(jīng)之路。目前,公司內(nèi)所有核心業(yè)務(wù)線都已接入全鏈路壓測(cè),月平均壓測(cè)次數(shù)達(dá)上萬次,幫助業(yè)務(wù)平穩(wěn)地度過了大大小小若干場(chǎng)高峰流量的沖擊。
解決方案
Quake (雷神之錘)作為公司級(jí)的全鏈路壓測(cè)平臺(tái),它的目標(biāo)是提供對(duì)整條鏈路進(jìn)行全方位、安全、真實(shí)的壓測(cè),來幫助業(yè)務(wù)做出更精準(zhǔn)的容量評(píng)估。因此我們對(duì) Quake 提出了如下的要求:
- 提供模擬線上真實(shí)流量的能力
- 壓測(cè)和 DDoS 攻擊不同的是,壓測(cè)有應(yīng)用場(chǎng)景,而 DDoS 可能只需要一個(gè)請(qǐng)求。為了更真實(shí)的還原用戶行為,我們需要獲取線上的真實(shí)流量進(jìn)行壓測(cè)。
- 具備快速創(chuàng)建壓測(cè)環(huán)境的能力
- 這里的環(huán)境指的是線上環(huán)境,因?yàn)槿绻麎簻y(cè)的是線下環(huán)境,即使不考慮“機(jī)器配置是否相同”這個(gè)因素,像集群規(guī)模、數(shù)據(jù)庫體量、網(wǎng)絡(luò)條件等這些因素,在線下環(huán)境下都無法進(jìn)行模擬,這樣得出壓測(cè)結(jié)果,其參考價(jià)值并不大。
- 支持多種壓測(cè)類型
- 壓測(cè)類型除了支持標(biāo)準(zhǔn)的 HTTP 協(xié)議,還需要對(duì)美團(tuán)內(nèi)部的 RPC 和移動(dòng)端協(xié)議進(jìn)行支持。
- 提供壓測(cè)過程的實(shí)時(shí)監(jiān)控與過載保護(hù)
- 全鏈路壓測(cè)是一個(gè)需要實(shí)時(shí)關(guān)注服務(wù)狀態(tài)的過程,尤其在探測(cè)極限的時(shí)候,需要具備精準(zhǔn)調(diào)控 QPS 的能力,秒級(jí)監(jiān)控的能力,預(yù)設(shè)熔斷降級(jí)的能力,以及快速定位問題的能力。
Quake 整體架構(gòu)設(shè)計(jì)
Quake 集數(shù)據(jù)構(gòu)造、壓測(cè)隔離、場(chǎng)景管理、動(dòng)態(tài)調(diào)控、過程監(jiān)控、壓測(cè)報(bào)告為一體,壓測(cè)流量盡量模擬真實(shí),具備分布式壓測(cè)能力的全鏈路壓測(cè)系統(tǒng),通過模擬海量用戶真實(shí)的業(yè)務(wù)操作場(chǎng)景,提前對(duì)業(yè)務(wù)進(jìn)行高壓力測(cè)試,全方位探測(cè)業(yè)務(wù)應(yīng)用的性能瓶頸,確保平穩(wěn)地應(yīng)對(duì)業(yè)務(wù)峰值。
架構(gòu)圖:
Quake 整體架構(gòu)上分為:
- Quake-Web:壓測(cè)管理端,負(fù)責(zé)壓測(cè)數(shù)據(jù)構(gòu)造、壓測(cè)環(huán)境準(zhǔn)備、場(chǎng)景管理、壓測(cè)過程的動(dòng)態(tài)調(diào)整以及壓測(cè)報(bào)表展示等。
- Quake-Brain:調(diào)度中心,負(fù)責(zé)施壓資源的調(diào)度、任務(wù)分發(fā)與機(jī)器資源管理。
- Quake-Agent:壓測(cè)引擎,負(fù)責(zé)模擬各種壓測(cè)流量。
- Quake-Monitor:監(jiān)控模塊,統(tǒng)計(jì)壓測(cè)結(jié)果,監(jiān)控服務(wù)各項(xiàng)指標(biāo)。
管理端核心功能
數(shù)據(jù)構(gòu)造
傳統(tǒng)的數(shù)據(jù)構(gòu)造,一般由測(cè)試人員自己維護(hù)一批壓測(cè)數(shù)據(jù)。但這種方式存在很大的弊端,一方面維護(hù)成本相對(duì)較高,另一方面,其構(gòu)造出的數(shù)據(jù)多樣性也不足夠。在真實(shí)業(yè)務(wù)場(chǎng)景中,我們需要的是能直接回放業(yè)務(wù)高峰期產(chǎn)生的流量,只有面對(duì)這樣的流量沖擊,才能真實(shí)的反映系統(tǒng)可能會(huì)產(chǎn)生的問題。
Quake 主要提供了 HTTP 和 RPC 的兩種數(shù)據(jù)構(gòu)造方式:
HTTP 服務(wù)的訪問日志收集
對(duì)于 HTTP 服務(wù),在 Nginx 層都會(huì)產(chǎn)生請(qǐng)求的訪問日志,我們對(duì)這些日志進(jìn)行了統(tǒng)一接入,變成符合壓測(cè)需要的流量數(shù)據(jù)。架構(gòu)圖如下:
S3為最終日志存儲(chǔ)平臺(tái)
底層使用了 Hive 作為數(shù)倉(cāng)的工具,使業(yè)務(wù)在平臺(tái)上可以通過簡(jiǎn)單的類 SQL 語言進(jìn)行數(shù)據(jù)構(gòu)造。Quake 會(huì)從數(shù)倉(cāng)中篩選出相應(yīng)的數(shù)據(jù),作為壓測(cè)所需的詞表文件,將其存儲(chǔ)在 S3 中。
詞表:壓測(cè)所需的元數(shù)據(jù),每一行代表一個(gè)請(qǐng)求,包含請(qǐng)求的 method、path、params、header、body等等。
RPC 線上流量實(shí)時(shí)錄制
對(duì)于 RPC 服務(wù),服務(wù)調(diào)用量遠(yuǎn)超 HTTP 的量級(jí),所以在線上環(huán)境不太可能去記錄相應(yīng)的日志。這里我們使用對(duì)線上服務(wù)進(jìn)行實(shí)時(shí)流量錄制,結(jié)合 RPC 框架提供的錄制功能,對(duì)集群中的某幾臺(tái)機(jī)器開啟錄制,根據(jù)要錄制的接口和方法名,將請(qǐng)求數(shù)據(jù)上報(bào)到錄制流量的緩沖服務(wù)(Broker)中,再由 Broker 生成最終的壓測(cè)詞表,上傳到存儲(chǔ)平臺(tái)(S3)。
- RPC Client:服務(wù)的調(diào)用方
- Server:服務(wù)提供方
- Broker:錄制后流量緩沖服務(wù)器
- S3:流量最終存儲(chǔ)平臺(tái)
其他優(yōu)化:
流量參數(shù)偏移
有些場(chǎng)景下,構(gòu)造出來的流量是不能直接使用的,我們需要對(duì)用戶 ID、手機(jī)號(hào)等信息進(jìn)行數(shù)據(jù)偏移。Quake 也是提供了包含四則運(yùn)算、區(qū)間限定、隨機(jī)數(shù)、時(shí)間類型等多種替換規(guī)則。
詞表文件的分片
數(shù)據(jù)構(gòu)造產(chǎn)生的詞表文件,我們需要進(jìn)行物理上的分片,以保證每個(gè)分片文件大小盡可能均勻,并且控制在一定大小之內(nèi)。這么做的主要原因是,后續(xù)壓測(cè)肯定是由一個(gè)分布式的壓測(cè)集群進(jìn)行流量的打入,考慮到單機(jī)拉取詞表的速度和加載詞表的大小限制,如果將詞表進(jìn)行分片的話,可以有助于任務(wù)調(diào)度更合理的進(jìn)行分配。
壓測(cè)隔離
做線上壓測(cè)與線下壓測(cè)最大不同在于,線上壓測(cè)要保證壓測(cè)行為安全且可控,不會(huì)影響用戶的正常使用,并且不會(huì)對(duì)線上環(huán)境造成任何的數(shù)據(jù)污染。要做到這一點(diǎn),首要解決的是壓測(cè)流量的識(shí)別與透?jìng)鲉栴}。有了壓測(cè)標(biāo)識(shí)后,各服務(wù)與中間件就可以依據(jù)標(biāo)識(shí)來進(jìn)行壓測(cè)服務(wù)分組與影子表方案的實(shí)施。
鏈路診斷
由于鏈路關(guān)系的復(fù)雜性,一次壓測(cè)涉及的鏈路可能非常復(fù)雜。很多時(shí)候,我們很難確認(rèn)間接依賴的服務(wù)又依賴了哪些服務(wù),而任何一個(gè)環(huán)節(jié)只要出現(xiàn)問題,比如某個(gè)中間件版本不達(dá)標(biāo),測(cè)試標(biāo)識(shí)就不會(huì)再往下進(jìn)行透?jìng)鳌uake 提供了鏈路匹配分析的能力,通過平臺(tái)試探性地發(fā)送業(yè)務(wù)實(shí)際需要壓測(cè)的請(qǐng)求,根據(jù) Mtrace提供的數(shù)據(jù),幫助業(yè)務(wù)快速定位到標(biāo)記透?jìng)魇〉姆?wù)節(jié)點(diǎn)。
鏈路診斷總覽
鏈路診斷詳情定位
壓測(cè)服務(wù)隔離
一些大型的壓測(cè)通常選擇在深夜低峰時(shí)期進(jìn)行,建議相關(guān)的人員要時(shí)刻關(guān)注各自負(fù)責(zé)的系統(tǒng)指標(biāo),以免影響線上的正常使用。而對(duì)于一些日常化的壓測(cè),Quake 提供了更加安全便捷的方式進(jìn)行。在低峰期,機(jī)器基本都是處于比較空閑的狀態(tài)。我們將根據(jù)業(yè)務(wù)的需求在線上對(duì)整條鏈路快速創(chuàng)建一個(gè)壓測(cè)分組,隔出一批空閑的機(jī)器用于壓測(cè)。將正常流量與測(cè)試流量在機(jī)器級(jí)別進(jìn)行隔離,從而降低壓測(cè)對(duì)服務(wù)集群帶來的影響。
依賴標(biāo)識(shí)透?jìng)鞯臋C(jī)制,在 Quake 平臺(tái)上提供了基于 IP、機(jī)器數(shù)、百分比不同方式的隔離策略,業(yè)務(wù)只需提供所需隔離的服務(wù)名,由 Quake 進(jìn)行一鍵化的開啟與關(guān)閉。
壓測(cè)數(shù)據(jù)隔離
還有一個(gè)比較棘手的問題是針對(duì)寫請(qǐng)求的壓測(cè),因?yàn)樗鼤?huì)向真實(shí)的數(shù)據(jù)庫中寫入大量的臟數(shù)據(jù)。我們借鑒了阿里最早提出的“影子表”隔離的方案。“影子表”的核心思想是,使用線上同一個(gè)數(shù)據(jù)庫,包括共享數(shù)據(jù)庫中的內(nèi)存資源,因?yàn)檫@樣才能更接近真實(shí)場(chǎng)景,只是在寫入數(shù)據(jù)時(shí)會(huì)寫在了另一張“影子表”中。
對(duì)于 KV 存儲(chǔ),也是類似的思路。這里講一下 MQ(消息隊(duì)列)的實(shí)現(xiàn),MQ 包括生產(chǎn)和消費(fèi)兩端,業(yè)務(wù)可以根據(jù)實(shí)際的需要選擇在生產(chǎn)端忽略帶測(cè)試標(biāo)識(shí)的消息,或者在消費(fèi)端接收消息后再忽略兩種選擇。
調(diào)度中心核心設(shè)計(jì)
調(diào)度中心作為整個(gè)壓測(cè)系統(tǒng)的大腦,它管理了所有的壓測(cè)任務(wù)和壓測(cè)引擎。基于自身的調(diào)度算法,調(diào)度中心將每個(gè)壓測(cè)任務(wù)拆分成若干個(gè)可在單臺(tái)壓測(cè)引擎上執(zhí)行的計(jì)劃,并將計(jì)劃以指令的方式下發(fā)給不同的引擎,從而執(zhí)行壓測(cè)任務(wù)。
資源計(jì)算
不同的壓測(cè)場(chǎng)景,需要的機(jī)器資源不一樣。以 HTTP 服務(wù)為例,在請(qǐng)求/響應(yīng)體都在 1K 以內(nèi),響應(yīng)時(shí)間在 50ms 以內(nèi)和 1s 左右的兩個(gè)請(qǐng)求,單個(gè)施壓機(jī)能達(dá)到的極限值完全不同。影響壓測(cè)能力的因素有很多,計(jì)算中心會(huì)依據(jù)壓測(cè)模型的不同參數(shù),進(jìn)行資源的計(jì)算。
主要參考的數(shù)據(jù)包括:
- 壓測(cè)期望到達(dá)的 QPS。
- 壓測(cè)請(qǐng)求的平均響應(yīng)時(shí)間和請(qǐng)求/響應(yīng)體大小。
- 壓測(cè)的詞表大小、分片數(shù)。
- 壓測(cè)類型。
- 所需壓測(cè)的機(jī)房。
事件注入機(jī)制
因?yàn)檎麄€(gè)壓測(cè)過程一直處在動(dòng)態(tài)變化之中,業(yè)務(wù)會(huì)根據(jù)系統(tǒng)的實(shí)際情況對(duì)壓力進(jìn)行相應(yīng)的調(diào)整。在整個(gè)過程中產(chǎn)生的事件類型比較多,包括調(diào)整 QPS 的事件、觸發(fā)熔斷的事件、開啟事故注入、開啟代碼級(jí)性能分析的事件等等,同時(shí)觸發(fā)事件的情況也有很多種,包括用戶手動(dòng)觸發(fā)、由于系統(tǒng)保護(hù)機(jī)制觸等等。所以,我們?cè)诩軜?gòu)上也做了相應(yīng)的優(yōu)化,其大致架構(gòu)如下:
在代碼設(shè)計(jì)層面,我們采用了觀察者和責(zé)任鏈模式,將會(huì)觸發(fā)事件的具體情況作為觀察主題,主題的訂閱者會(huì)視情況類型產(chǎn)生一連串執(zhí)行事件。而在執(zhí)行事件中又引入責(zé)任鏈模式,將各自的處理邏輯進(jìn)行有效的拆分,以便后期進(jìn)行維護(hù)和能力擴(kuò)充。
機(jī)器管理
調(diào)度中心管理了所有的施壓機(jī)資源,這些施壓機(jī)分布在北京、上海的多個(gè)機(jī)房,施壓機(jī)采用容器化方式進(jìn)行部署,為后續(xù)的動(dòng)態(tài)擴(kuò)容、施壓機(jī)灰度升級(jí)以及異常摘除的提供了基礎(chǔ)保障。
動(dòng)態(tài)擴(kuò)容
業(yè)務(wù)對(duì)壓測(cè)的需求有高低峰之分,所以平臺(tái)也需要事先部署一部分機(jī)器用于日常的業(yè)務(wù)壓測(cè)。當(dāng)業(yè)務(wù)申請(qǐng)資源不足時(shí),平臺(tái)會(huì)按需通過容器化方式動(dòng)態(tài)的進(jìn)行擴(kuò)容。這樣做的好處,一方面是節(jié)省機(jī)器資源,另一方面就是便于升級(jí)。不難想象,升級(jí)50臺(tái)機(jī)器相對(duì)升級(jí)200臺(tái)機(jī)器,前者付出的代價(jià)肯定更小一些。
灰度升級(jí)
整個(gè)機(jī)器池維護(hù)著幾百臺(tái)機(jī)器,如果需要對(duì)這些機(jī)器進(jìn)行升級(jí)操作,難度系數(shù)也比較高。我們以前的做法是,在沒有業(yè)務(wù)壓測(cè)的時(shí)候,將機(jī)器全部下線,然后再批量部署,整個(gè)升級(jí)過程既耗時(shí)又痛苦。為此,我們引入了灰度升級(jí)的概念,對(duì)每臺(tái)施壓機(jī)提供了版本的概念,機(jī)器選擇時(shí),優(yōu)先使用穩(wěn)定版的機(jī)器。根據(jù)機(jī)器目前使用的狀態(tài),分批替換未使用的機(jī)器,待新版本的機(jī)器跑完基準(zhǔn)和回歸測(cè)試后,將機(jī)器選擇的策略改為最新版。通過這種方式,我們可以讓整個(gè)升級(jí)過程,相對(duì)平順、穩(wěn)定,且能夠讓業(yè)務(wù)無感知。
異常摘除
調(diào)度中心維持了與所有施壓機(jī)的心跳檢測(cè),對(duì)于異常節(jié)點(diǎn)提供了摘除替換的能力。機(jī)器摘除能力在壓測(cè)過程中非常有必要,因?yàn)閴簻y(cè)期間,我們需要保證所有的機(jī)器行為可控。不然在需要降低壓力或停止壓測(cè)時(shí),如果施壓機(jī)不能正常做出響應(yīng),其導(dǎo)致的后果將會(huì)非常嚴(yán)重。
壓測(cè)引擎優(yōu)化
在壓測(cè)引擎的選擇上,Quake 選擇了自研壓測(cè)引擎。這也是出于擴(kuò)展性和性能層面的考慮,特別在擴(kuò)展性層面,主要是對(duì)各種協(xié)議的支持,這里不展開進(jìn)行闡述。性能方面,為了保證引擎每秒能產(chǎn)生足夠多的請(qǐng)求,我們對(duì)引擎做了很多性能優(yōu)化的工作。
性能問題
通常的壓測(cè)引擎,采用的是 BIO 的方式,利用多線程來模擬并發(fā)的用戶數(shù),每個(gè)線程的工作方式是:請(qǐng)求-等待-響應(yīng)。
通信圖:
這種方式主要的問題是,中間的等待過程,線程資源完全被浪費(fèi)。這種組合模式下,性能問題也會(huì)更嚴(yán)重(組合模式:即模擬用戶一連串的用戶行為,以下單為例,請(qǐng)求組中會(huì)包含用戶登錄、加入購(gòu)物車、創(chuàng)建訂單、支付訂單、查看支付狀態(tài)。這些請(qǐng)求彼此間是存在先后關(guān)系的,下一個(gè)請(qǐng)求會(huì)依賴于上一個(gè)請(qǐng)求的結(jié)果。),若請(qǐng)求組中有5個(gè)串聯(lián)請(qǐng)求,每個(gè)請(qǐng)求的時(shí)長(zhǎng)是200ms,那完成一組請(qǐng)求就需要 1s 。這樣的話,單機(jī)的最大 QPS 就是能創(chuàng)建的最大線程數(shù)。我們知道機(jī)器能創(chuàng)建的線程數(shù)有限,同時(shí)線程間頻繁切換也有成本開銷,致使這種通信方式能達(dá)到的單機(jī)最大 QPS 也很有限。
這種模型第二個(gè)問題是,線程數(shù)控制的粒度太粗,如果請(qǐng)求響應(yīng)很快,僅幾十毫秒,如果增加一個(gè)線程,可能 QPS 就上漲了將近100,通過增加線程數(shù)的方式無法精準(zhǔn)的控制 QPS,這對(duì)探測(cè)系統(tǒng)的極限來說,十分危險(xiǎn)。
IO 模型優(yōu)化
我們先看下 NIO 的實(shí)現(xiàn)機(jī)制,從客戶端發(fā)起請(qǐng)求的角度看,存在的 IO 事件分別是建立連接就緒事件(OP_CONNECT)、IO 就緒的可讀事件 (OP_READ) 和 IO 就緒的可寫事件(OP_WRITE),所有 IO 事件會(huì)向事件選擇器(Selector)進(jìn)行注冊(cè),并由它進(jìn)行統(tǒng)一的監(jiān)聽和處理,Selector 這里采用的是 IO 多路復(fù)用的方式。
在了解 NIO 的處理機(jī)制后,我們?cè)倏紤]看如何進(jìn)行優(yōu)化。整個(gè)核心思想就是根據(jù)預(yù)設(shè)的 QPS,保證每秒發(fā)出指定數(shù)量的請(qǐng)求,再以 IO 非阻塞的方式進(jìn)行后續(xù)的讀寫操作,取消了 BIO 中請(qǐng)求等待的時(shí)間。優(yōu)化后的邏輯如下:
優(yōu)化一:采用 Reactor 多線程模型
這里主要耗時(shí)都在 IO 的讀寫事件上,為了達(dá)到單位時(shí)間內(nèi)盡可能多的發(fā)起壓測(cè)請(qǐng)求,我們將連接事件與讀寫事件分離。連接事件采用單線程 Selector 的方式來處理,讀寫事件分別由多個(gè) Worker 線程處理,每個(gè) Worker 線程也是以 NIO 方式進(jìn)行處理,由各自的 Selector 處理 IO 事件的讀寫操作。這里每個(gè) Worker 線程都有自己的事件隊(duì)列,數(shù)據(jù)彼此隔離,這樣做主要是為了避免數(shù)據(jù)同步帶來的性能開銷。
優(yōu)化二:業(yè)務(wù)邏輯與 IO 讀寫事件分離
這里說的業(yè)務(wù)邏輯主要是針對(duì)請(qǐng)求結(jié)果的處理,包括對(duì)請(qǐng)求數(shù)據(jù)的采樣上報(bào),對(duì)壓測(cè)結(jié)果的解析校驗(yàn),對(duì)請(qǐng)求轉(zhuǎn)換率的匹配等。如果將這些邏輯放在 Worker 線程中處理,必然會(huì)影響 IO 讀取的速度。因?yàn)?Selector 在監(jiān)聽到 IO 就緒事件后,會(huì)進(jìn)行單線程處理,所以它的處理要盡可能的簡(jiǎn)單和快速,不然會(huì)影響其他就緒事件的處理,甚至造成隊(duì)列積壓和內(nèi)存問題。
內(nèi)存優(yōu)化
壓測(cè)引擎另一個(gè)重要的指標(biāo)是 Full GC 的時(shí)間,因?yàn)槿绻骖l繁出現(xiàn) Full GC,那會(huì)造成實(shí)際壓測(cè)曲線(QPS)的抖動(dòng),這種抖動(dòng)會(huì)放大被壓服務(wù)真實(shí)的響應(yīng)時(shí)間,造成真實(shí) QPS 在預(yù)設(shè)值的上下波動(dòng)。嚴(yán)重的情況,如果是長(zhǎng)時(shí)間出現(xiàn) Full GC,直接就導(dǎo)致預(yù)壓的 QPS 壓不上去的問題。
下面看一組 Full GC 產(chǎn)生的壓測(cè)曲線:
為了解決 GC 的問題,主要從應(yīng)用自身的內(nèi)存管理和 JVM 參數(shù)兩個(gè)維度來進(jìn)行優(yōu)化。
合理分配內(nèi)存對(duì)象
請(qǐng)求對(duì)象加載機(jī)制優(yōu)化
引擎首先加載詞表數(shù)據(jù)到內(nèi)存中,然后根據(jù)詞表數(shù)據(jù)生成請(qǐng)求對(duì)象進(jìn)行發(fā)送。對(duì)于詞表數(shù)據(jù)的加載,需要設(shè)置一個(gè)大小上限,這些數(shù)據(jù)是會(huì)進(jìn)入“老年代”,如果“老年代”占用的比例過高,那就會(huì)頻發(fā)出現(xiàn) Full GC 的情況。這里對(duì)于詞表數(shù)據(jù)過大的情況,可以考慮采用流式加載的方式,在隊(duì)列中維持一定數(shù)量的請(qǐng)求,通過邊回放邊加載的方式來控制內(nèi)存大小。
請(qǐng)求對(duì)象的快用快銷
引擎在實(shí)際壓測(cè)過程中,假設(shè)單機(jī)是 1W 的 QPS,那它每秒就會(huì)創(chuàng)建 1W 個(gè)請(qǐng)求對(duì)象,這些對(duì)象可能在下一秒處理完后就會(huì)進(jìn)行銷毀。如果銷毀過慢,就會(huì)造成大量無效對(duì)象晉升老年代,所以在對(duì)響應(yīng)結(jié)果的處理中,不要有耗時(shí)的操作,保證請(qǐng)求對(duì)象的快速釋放。
這里放棄對(duì)象復(fù)用的原因是,請(qǐng)求的基本信息占用的內(nèi)存空間比較小。可一旦轉(zhuǎn)換成了待發(fā)送對(duì)象后,占用的內(nèi)存空間會(huì)比原始數(shù)據(jù)大很多,在 HTTP 和 RPC 服務(wù)中都存在同樣的問題。而且之前使用 Apache HttpAsyncClient 作為 HTTP 請(qǐng)求的異步框架時(shí),發(fā)現(xiàn)實(shí)際請(qǐng)求的 Response 對(duì)象掛在請(qǐng)求對(duì)象身上。也就是說一個(gè)請(qǐng)求對(duì)象在接收到結(jié)果后,該對(duì)象內(nèi)存增加了響應(yīng)結(jié)果的空間占用,如果采用復(fù)用請(qǐng)求對(duì)象的方式,很容易造成內(nèi)存泄露的問題。
JVM 參數(shù)調(diào)優(yōu)
這里以 JVM 的 CMS 收集器為例,對(duì)于高并發(fā)的場(chǎng)景,瞬間產(chǎn)生大量的對(duì)象,這些對(duì)象的存活時(shí)間又非常短,我們需要:
- 適當(dāng)增大新生代的大小,保證新生代有足夠的空間來容納新產(chǎn)生的對(duì)象。當(dāng)然如果老年代設(shè)置的過小,會(huì)導(dǎo)致頻繁的 Full GC。
- 適當(dāng)調(diào)大新生代向晉升老年代的存活次數(shù),減少無效對(duì)象晉升老年代的機(jī)率;同時(shí)控制新生代存活區(qū)的大小,如果設(shè)置的過小,很容易造成那些無法容納的新生代對(duì)象提前晉升。
- 提前觸發(fā)老年代的 Full GC,因?yàn)槿绻却夏甏鷿M了再開始回收,可能會(huì)太晚,這樣很容易造成長(zhǎng)時(shí)間的 Full GC。一般設(shè)在 70% 的安全水位進(jìn)行回收。而且回收的時(shí)候,需要觸發(fā)一次 Young GC,這可以減少重新標(biāo)記階段應(yīng)用暫停的時(shí)間,另一方面,也防止在回收結(jié)束后,有大量無效的對(duì)象進(jìn)入老年代中。
- 設(shè)置需要進(jìn)行內(nèi)存壓縮整理的 GC 次數(shù),內(nèi)存整理,很多時(shí)候是造成長(zhǎng)時(shí)間 GC 的主要原因。因?yàn)閮?nèi)存整理是采用 Serial Old 算法,以單線程的方式進(jìn)行處理,這個(gè)過程會(huì)非常慢。尤其是在老年代空間不足的情況下,GC 的時(shí)間會(huì)變得更長(zhǎng)。
監(jiān)控模塊
壓測(cè)肯定會(huì)對(duì)線上服務(wù)產(chǎn)生一定的影響,特別是一些探測(cè)系統(tǒng)極限的壓測(cè),我們需要具備秒級(jí)監(jiān)控的能力,以及可靠的熔斷降級(jí)機(jī)制。
客戶端監(jiān)控
壓測(cè)引擎會(huì)將每秒的數(shù)據(jù)匯總后上報(bào)給監(jiān)控模塊,監(jiān)控模塊基于所有上報(bào)來的數(shù)據(jù)進(jìn)行統(tǒng)計(jì)分析。這里的分析需要實(shí)時(shí)進(jìn)行處理,這樣才能做到客戶端的秒級(jí)監(jiān)控。監(jiān)控的數(shù)據(jù)包括各 TP 線的響應(yīng)情況、QPS 曲線波動(dòng)、錯(cuò)誤率情況以及采樣日志分析等等。
實(shí)時(shí) QPS 曲線
錯(cuò)誤率統(tǒng)計(jì)
采樣日志
服務(wù)端監(jiān)控
除了通過引擎上報(bào)的壓測(cè)結(jié)果來進(jìn)行相應(yīng)的監(jiān)控分析之外,Quake 還集成了公司內(nèi)部統(tǒng)一的監(jiān)控組件,有監(jiān)控機(jī)器指標(biāo)的 Falcon 系統(tǒng)(小米開源),還有監(jiān)控服務(wù)性能的 CAT系統(tǒng)(美團(tuán)已經(jīng)開源)。Quake 提供了統(tǒng)一的管理配置服務(wù),讓業(yè)務(wù)能在 Quake 上方便觀察整個(gè)系統(tǒng)的健康狀況。
熔斷保護(hù)機(jī)制
Quake 提供了客戶端和服務(wù)端兩方面的熔斷保護(hù)措施。
首先是客戶端熔斷,根據(jù)業(yè)務(wù)自定義的熔斷闕值,Quake 會(huì)實(shí)時(shí)分析監(jiān)控?cái)?shù)據(jù),當(dāng)達(dá)到熔斷闕值時(shí),任務(wù)調(diào)度器會(huì)向壓測(cè)引擎發(fā)送降低 QPS 或者直接中斷壓測(cè)的指令,防止系統(tǒng)被壓掛。
被壓服務(wù)同樣也提供了熔斷機(jī)制,Quake 集成了公司內(nèi)部的熔斷組件(Rhino),提供了壓測(cè)過程中的熔斷降級(jí)和限流能力。與此同時(shí),Quake 還提供了壓測(cè)故障演練的能力,在壓測(cè)過程中進(jìn)行人為的故障注入,來驗(yàn)證整個(gè)系統(tǒng)的降級(jí)預(yù)案。
項(xiàng)目總結(jié)
最后,總結(jié)一下做 Quake 這個(gè)項(xiàng)目的一些心得。
小步快跑
其實(shí)在 Quake 出來之前,美團(tuán)公司內(nèi)部已有一個(gè)壓測(cè)平臺(tái)(Ptest ),它的定位是針對(duì)單服務(wù)的性能壓測(cè)。我們分析了 Ptest 平臺(tái)存在的一些問題,其壓測(cè)引擎能力也非常有限。在美團(tuán)發(fā)展早期,如果有兩個(gè)大業(yè)務(wù)線要進(jìn)行壓測(cè)的話,機(jī)器資源往往會(huì)不足,這需要業(yè)務(wù)方彼此協(xié)調(diào)。因?yàn)闇?zhǔn)備一次壓測(cè),前期投入成本太高,用戶需要自己構(gòu)造詞表,尤其是 RPC 服務(wù),用戶還需要自己上傳 IDL 文件等等,非常繁瑣。
Quake 針對(duì)業(yè)務(wù)的這些痛點(diǎn),整個(gè)團(tuán)隊(duì)大概花費(fèi)一個(gè)多月的時(shí)間開發(fā)出了第一個(gè)版本,并且快速實(shí)現(xiàn)了上線。當(dāng)時(shí),正面臨貓眼十一節(jié)前的一次壓測(cè),那也是 Quake 的第一次亮相,而且取得了不錯(cuò)的成績(jī)。后續(xù),我們基本平均兩周實(shí)現(xiàn)一次迭代,然后逐步加入了機(jī)器隔離、影子表隔離、數(shù)據(jù)偏移規(guī)則、熔斷保護(hù)機(jī)制、代碼級(jí)別的性能分析等功能。
快速響應(yīng)
項(xiàng)目剛線上時(shí),客服面臨問題非常多,不僅有使用層面的問題,系統(tǒng)自身也存在一些 Bug 缺陷。當(dāng)時(shí),一旦遇到業(yè)務(wù)線大規(guī)模的壓測(cè),我們團(tuán)隊(duì)都是全員待命,直接在現(xiàn)場(chǎng)解決問題。后續(xù)系統(tǒng)穩(wěn)定后,我們組內(nèi)采用了客服輪班制度,每個(gè)迭代由一位同學(xué)專門負(fù)責(zé)客服工作,保障當(dāng)業(yè)務(wù)遇到的問題能夠做到快速響應(yīng)。尤其是在項(xiàng)目上線初期,這點(diǎn)非常有必要。如果業(yè)務(wù)部門使用體驗(yàn)欠佳,項(xiàng)目口碑也會(huì)變差,就會(huì)對(duì)后續(xù)的推廣造成很大的問題。
項(xiàng)目推廣
這應(yīng)該是所有內(nèi)部項(xiàng)目都會(huì)遇到的問題,很多時(shí)候,推廣成果決定項(xiàng)目的生死。前期我們先在一些比較有代表性的業(yè)務(wù)線進(jìn)行試點(diǎn)。如果在試點(diǎn)過程中遇到的問題,或者業(yè)務(wù)同學(xué)提供的一些好的想法和建議,我們能夠快速地進(jìn)行迭代與落地。然后再不斷地?cái)U(kuò)大試點(diǎn)范圍,包括美團(tuán)外賣、貓眼、酒旅、金融等幾個(gè)大的 BG 都在 Quake 上進(jìn)行了幾輪全流程、大規(guī)模的全鏈路壓測(cè)。
隨著 Quake 整體功能趨于完善,同時(shí)解決了 Ptest(先前的壓測(cè)系統(tǒng))上的多個(gè)痛點(diǎn),我們逐步在各個(gè)業(yè)務(wù)線進(jìn)行了全面推廣和內(nèi)部培訓(xùn)。從目前收集的數(shù)據(jù)看,美團(tuán)超過 90% 的業(yè)務(wù)已從 Ptest 遷移到了 Quake 。而且整體的統(tǒng)計(jì)數(shù)據(jù),也比 Ptest 有了明顯的提升。
開放生態(tài)
Quake 目標(biāo)是打造全鏈路的壓測(cè)平臺(tái),但是在平臺(tái)建設(shè)這件事上,我們并沒有刻意去追求。公司內(nèi)部也有部分團(tuán)隊(duì)走的比較靠前,他們也做一些很多“試水性”的工作。這其實(shí)也是一件好事,如果所有事情都依托平臺(tái)來完成,就會(huì)面臨做不完的需求,而且很多事情放在平臺(tái)層面,也可能無解。
同時(shí),Quake 也提供了很多 API 供其他平臺(tái)進(jìn)行接入,一些業(yè)務(wù)高度定制化的工作,就由業(yè)務(wù)平臺(tái)獨(dú)自去完成。平臺(tái)僅提供基礎(chǔ)的能力和數(shù)據(jù)支持,我們團(tuán)隊(duì)把核心精力聚焦在對(duì)平臺(tái)發(fā)展更有價(jià)值的事情上。
跨團(tuán)隊(duì)合作
其實(shí),全鏈路壓測(cè)整個(gè)項(xiàng)目涉及的團(tuán)隊(duì)非常之多,上述提到的很多組件都需要架構(gòu)團(tuán)隊(duì)的支持。在跨團(tuán)隊(duì)的合作層面,我們應(yīng)該有“雙贏”的心態(tài)。像 Quake 平臺(tái)使用的很多監(jiān)控組件、熔斷組件以及性能分析工具,有一些也是兄弟團(tuán)隊(duì)剛線上沒多久的產(chǎn)品。 Quake 將其集成到平臺(tái)中,一方面是減少自身重復(fù)造輪子;另一方面也可以幫助兄弟團(tuán)隊(duì)推動(dòng)產(chǎn)品的研發(fā)工作。
作者簡(jiǎn)介
- 耿杰,美團(tuán)點(diǎn)評(píng)高級(jí)工程師。2017年加入美團(tuán)點(diǎn)評(píng),先后負(fù)責(zé)全鏈路壓測(cè)項(xiàng)目和 MagicDB 數(shù)據(jù)庫代理項(xiàng)目,目前主要負(fù)責(zé)這兩個(gè)項(xiàng)目的整體研發(fā)和推廣工作,致力于提升公司的整體研發(fā)效率與研發(fā)質(zhì)量。
招聘
團(tuán)隊(duì)長(zhǎng)期招聘 Java、Go、算法、AI 等技術(shù)方向的工程師,Base 北京、上海,歡迎有興趣的同學(xué)投遞簡(jiǎn)歷到gengjie02@meituan.com。
總結(jié)
以上是生活随笔為你收集整理的全链路压测平台(Quake)在美团中的实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud Zuul的fa
- 下一篇: Nacos部署中的一些常见问题汇总