春節保衛戰:騰訊百萬 QPS 線上環境雲壓測方案解析

圖片

導語|春節期間騰訊大部分業務進入流量備戰的緊張時刻。壓測相比於監控而言,是更具主動性的籌備手段。通過高負載、真實流量的預演,探測系統的瓶頸和發現風險,是服務質量保障體系的重要一環。雲壓測主要聚焦在壓測平臺的發壓端基礎能力構建,本文作者張澤強分享雲壓測備戰春節期間從壓測模型選型、用例編寫、測試數據構建到壓測報表分析的壓測方案。期望對你有幫助。

目錄

1 背景與挑戰

2 解決方案

2.1 壓測模式選型

2.2 壓測用例編寫

2.3 測試數據構造

2.4 壓測報表分析

3 實踐案例

3.1 手Q春保活動

3.2 視頻業務容災演練

4 總結展望

01、背景與挑戰

春節期間,騰訊大部分業務進入春保備戰的緊張時刻。節假日高峯時間點上漲五倍十倍的用戶流量,給業務穩定性帶來不少的挑戰。以各位熟知的QQ爲例,QQ服務大規模的移動互聯網用戶,作爲一個超大流量應用,面對逢年過節的流量洪峯是它不可忽視的問題。手Q業務每年元旦和春節的0點0分,都會有一波非常高的尖峯。讀鏈路和寫鏈路分不同命令字會數倍地流量飆升。而在線視頻業務也面臨同樣的問題,通過做好容災演習以驗證在各種異常情況下的容災容錯能力,通過壓測排查關鍵服務性能是否存在問題、找到鏈路性能瓶頸、明確鏈路服務擴容模型等任務迫在眉睫。

壓測相比於監控而言,是更具主動性的防備手段。通過高負載、真實流量的預演,用於探測系統的瓶頸和發現風險,是服務保障體系的重要一環。整體來看,雲壓測的主要應用場景包括:

  • 第一,驗證新功能上線的吞吐量預期,保障系統穩定性,避免服務上線的流量瞬時擊穿

  • 第二,老舊服務的重構改造。在降本增效的背景下,如何降低機器的部署成本以帶來顯著收益,是個值得思考的問題。老舊服務大部分存在業務流量構造的難題,大部分也不存在存量的用例自動化驗證機制,會帶來比較大的重構挑戰。

  • 第三,發現系統瓶頸缺陷以保障大型節點系統穩定性。春節、元旦、618、雙11等大型活動預演,提前通過全鏈路壓測發現系統瓶頸和缺陷,按照保障目標提前進行擴容、縮容。擴容是爲了直接提高系統可處理的最大吞吐量,而縮容是爲了驗證該服務存在冗餘的資源配額,在上游的處理能力跟不上的時候,該資源是浪費的。

  • 第四,驗證後臺服務降級、彈性策略。部分業務在服務啓動時存在資源預熱加載、CPU使用率飆升、OOM等問題。這類問題大部分發生在業務流量比較大的情況下,平時不容易模擬,通過壓測將流量線上放大能夠有效的復現該場景。

然而目前,大部分業務壓測是單機器、單服務、單鏈路的流量模擬,在容量預估場景下容易出現偏差,主要問題如下:

  • 現有系統大部分是微服務體系,存在上下游的鏈路依賴,第三方的鏈路不能直接壓測(支付、雲廠商服務)。很多情況下整個服務的瓶頸,不在當前壓測服務。而直接採用mockserver 來模擬耗時和返回業務數據,也會隱藏該服務短板。

  • 其次是流量構造失真問題。線上的用戶量、關係鏈、請求參數的維度比較多,無法直接通過編寫用例腳本(等價有限的參數構造邏輯)來模擬線上真實流量。固定化參數數據直接導致熱點數據異常,也會導致壓測失真,無法有效通過局部推算全局的表現。

  • 此外,數據規模沒有達到預期。大部分服務屬於I/O密集型服務,業務瓶頸都在存儲層服務,例如mysql、redis、kafka等中間件,是由於持續量變導致的質變。數據規模在翻番的情況下,上下游鏈路的耗時表現可能呈現出雪崩效應。

因此要準確預估服務的容量,在高併發的場景下探測瓶頸和缺陷並不是簡單的事情。工欲善其事,必先利其器。如何設計、實現一款好的壓測工具並且給業務降低接入成本,是一件持之以恆的事情。

以前面提到的移動端QQ和在線視頻爲例。手Q在春節期間讀鏈路和寫鏈路分不同命令字會數倍的流量飆升。其中,針對只讀鏈路可以通過集羣流量調度做到讀鏈路的壓測,但是寫鏈路的壓測(主要包含 feed 發表、評論、點贊等交互)較缺乏,測試數據構造複雜,用例編寫成本高,是一個亟需解決的問題;此外,雲壓測之前主要採用JS腳本進行場景編排,圖文場景出現了內存佔用高,二進制頻繁進行數據深拷貝,也給壓測機內存資源準備帶來不少的壓力。

下面章節我們會詳細解析雲壓測的解決方案,最後分享其在發壓端支持手Q和在線視頻業務的實踐案例。本文對雲壓測的思考和實踐,供拋磚引玉。歡迎繼續閱讀。

02、解決方案

雲壓測服務的目標是模擬海量用戶的真實場景,全方位驗證系統可用性和穩定性。簡化性能測試工具,讓用戶更加聚焦業務和性能問題本身。從用戶的使用角度,常規的壓測流程主要包括以下,其中定位和分析瓶頸是最具備價值一環:

圖片

實施路徑:

  • 通過全鏈路壓測精準評估系統上下游服務容量進行擴容、縮容,減少機器部署配額;

  • 通過壓測將性能測試左移,提前定位和分析性能瓶頸,保障服務穩定性。

2.1 壓測模式選型

雲壓測提供併發模式和RPS (request per second)模式,無論哪種模式的目標都是給被壓服務帶來足夠的吞吐量壓力。RPS模式底層也依賴併發(Virtual User)。通常壓測引擎通過多線程、協程模擬多個客戶端同時請求,保障單位時間內的吞吐量穩定,通過梯度、手動調速調節目標流量壓力。

接下來講講VU和RPS的簡單換算公式。其中RPS表現跟接口耗時直接相關。假設接口耗時爲100ms,1個VU平均一秒能夠請求10次,那麼在發壓機、後臺服務資源充裕的前提,VU和RPS是線性遞增關係。

圖片

  • 處在線性增長區時,響應時間(RT)基本穩定,吞吐量(RPS)隨着併發用戶數(VU)的增加而增加。

  • 三者關係符合Little定律:VU=RPS*RT。隨着VU增大、系統的資源利用率飽和,系統到達拐點。

  • 若繼續增大VU,響應時間開始增大,RPS開始下降。繼續增加VU,系統超負荷、進入過飽和區,此時響應時間急劇增大、RPS急劇下降。

大部分後臺服務壓測適用於RPS模式。該服務或功能模塊一般用於滿足多少吞吐量的要求,因此主要觀測後臺服務的處理請求速率。支持用戶自定義在報表頁面進行手動調速,例如用戶預期是1W RPS,可自定義初始1000RPS,按照一定的階梯(1~2的係數遞增)進行調速,這樣通過觀測服務本身的業務指標(吞吐量、時延、錯誤率)和飽和度(內存、CPU使用率)即可分析業務的瓶頸。針對秒殺、元旦、春節零點等活動,需要模擬多個用戶的同時併發場景的話,則可以採用併發模式,例如支持10w人同時的活動搶購。

壓測的併發調度下幾種常見場景:

  • 場景一:接口耗時敏感,併發量小,時延失真

在接口耗時比較低的情況下,會出現80併發和250併發的吞吐量一致的情況,原因是發壓機調度的機器規格一致並且CPU負載飽和。從下方的壓測指標圖可以看出,併發數逐漸增大。當併發達到80、達到吞吐量最大值,後續隨着併發增大,響應耗時急劇增加,而吞吐量卻沒有明顯變化。這其實意味當前這臺發壓機已經達到極限,cpu、內存資源水位位於高處。隨着併發數增大,該機器沒有更多的處理資源,因此協程頻繁上下文切換帶來發壓機的請求耗時的增加。壓測報告的latency耗時表現失真,而服務端的耗時表現正常。當發壓機的資源達到瓶頸時或者CPU使用率超過 80%,指標採集線程、協程會出現頻繁CPU時鐘中斷,導致指標埋點採集延遲。

壓測報告表現:

圖片

發壓機負載表現:

圖片

  • 場景二:壓測機&被壓服務資源充裕,RPS達不到預期目標

壓測過程中,併發數配置比較充裕,發壓機負載穩定,後臺服務也沒達到探測瓶頸,但是RPS一直上不去。值得注意的是,RPS並不等價TPS。作爲發壓側,引擎能夠保證每秒發出去的請求數,TPS可以理解爲收到回包的時間點數據,隨着不同接口的耗時變化,吞吐量抖動會比較明顯,表現出來爲用戶設置的RPS和實際TPS有差異。

圖片

因此TPS會隨着耗時的變化而頻繁抖動。以http協議爲例,客戶端完整的耗時路徑包括DNS尋址->建立連接->請求包網絡傳輸時間->服務端處理耗時->響應包網絡傳輸耗時,因此吞吐量表現會比APM監控的耗時要長,針對高性能組件壓測場景,網絡往返耗時波動佔整體耗時比例會更高,導致不同地域下的吞吐量表現可能相差一倍。

圖片

2.2 壓測用例編寫

雲壓測主要面向專項測試人員(對外TOB交付壓測報告)、後臺研發人員(保障後臺服務質量),技術運營開發者(產品上線吞吐量驗收),這部分開發者的代碼編寫水平參差,無法通過一套方案來滿足用戶訴求。因此雲壓測提供了多種用例編寫方式(低代碼、JS、GO、xml),以滿足不同用戶、不同場景下的適配需求。下面將分別闡述3個模式。

2.2.1 模式一:JS腳本模式

JS腳本模式提供串聯接口的編排模式,平臺通過封裝公司內的常用協議提供對應的腳本模板,業務可以基於該模板進行請求參數 DIY降低接入成本。JS是高級語言、相關開源社區活躍,語言本身解釋能力比較強,針對後臺單接口、通用協議具備一定的通用性,也是業內主流開源引k6主打的業務場景。其缺點也比較明顯,開源協作的社區氛圍下,大部分後臺研發人員對JS語法和基礎框架並不是特別熟悉,無法吸引更多的研發人員持續迭代。大量的私有協議適配、基礎庫封裝會帶來大量的適配工作量(goja的腳本映射),依賴平臺方、需求方持續迭代,而這些私有場景的適配工作並不能直接複用提高 ROI,無法快速支持業務特定需求。

// Send a http get request

import http from 'pts/http'; // 協議適配模塊

import { check, sleep } from 'pts'; // 常規編排流程封裝

 

export default function () {

    // simple get request

    const resp1 = http.get('http://httpbin.org/get'); // 執行發包操作

    console.log(resp1.body); // 日誌打印用於問題定位

    // if resp1.body is a json string, resp1.json() transfer json format body to a json object

    console.log(resp1.json());

    check('status is 200', () => resp1.statusCode === 200); // 通過斷言集成業務指標

}

2.2.2 模式二:Go Plugin模式

它提供Go的腳本編寫方案。該方案是Go官方支持的熱加載機制,對第三方依賴具備較大的侷限性(無法依賴不同版本,編譯環境、執行環境需要統一,存在cgo依賴),因此業界並沒有大規模使用。但是雲壓測場景下,可以滿足用戶go用例編寫訴求,以及通過依賴倒置注入規避第三方版本問題,現有用戶場景下使用也表現成熟。複雜編排、私有協議的支持給予了用戶一定的空間,引擎本身只需要保證指標埋點、併發調度符合用戶設置就能滿足訴求。

由於歷史包袱,protobuf協議很多單文件上千行,接口的參數構造複雜,存在公共協議之間的嵌套。用JS或python動態語言腳本編寫用例,需要通過上傳所有協議文件,用json來轉換protobuf二進制數據,管理目錄嵌套層級。這給用戶帶來很大的心理負擔,同時也喪失stub代碼的優勢,減少數據頻繁轉換、構造參數。

// Init 從用戶腳本注入底層實現,包括加載 metrics 上報插件&注入 otel 實現框架。
var Init = plugin.Init

// Run 核心腳本編寫邏輯,引擎會按照壓測模型執行該 Run 函數。
func Run(ctx context.Context) error {
    // 必須通過 NewRequestWithContext 傳入 ctx 構造請求,否則無法展示 har 格式的採樣數據
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpbin.org/get", nil)
    if err != nil {
        return err
    }
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    // 自定義斷言,自動上報業務指標
    assert.True(ctx, "status code is 200", func() bool {
        return resp.StatusCode == http.StatusOK
    })    
    return nil
}

Go Plugin複雜場景下的優勢:

  • 支持插樁代碼引用,無需額外上傳協議文件,減少頻繁數據序列化操作帶來的性能損耗;

  • 對後臺開發者友好,基於go進行用例編寫,可以更靈活地複雜場景編排,支持現有存量協議封裝、工具庫集成,提高代碼複用效率;

  • 平臺集成 HTTP、gRPC等協議指標採集,用戶聚焦在用例編排,請求流量構造等場景,減少用戶心智負擔。

2.2.3 模式三:低代碼、JMeter(GUI)的編寫模式

雲壓測面向專項測試人員、非開發出身的從業者提供了UI拖拽的方案。基於har to js的模板映射框架,用戶可自定義在簡單模式、腳本模式進行切換,目前支持har->js的單向轉換。針對http等通用標準協議,提供 request、header、config參數構造選項,降低用戶的心智負擔和使用雲壓測的門檻。而存量用例也是重要考量因素,JMeter在性能測試領域的市場佔用率比較高,大部分的商業化測試軟件也會支持jmx腳本壓測,業務的存量測試資產轉移到全新平臺有腳本適配改造成本,整體的投入產出比不高。因此雲壓測也基於JMeter插件機制進行擴展,新增指標埋點、日誌採樣、線程組調度的能力,能夠在一套調度平臺(box、ship機制)執行不同的發壓引擎,降低整體適配的複雜度。

圖片

2.3 測試數據構造

測試數據構造是用例編寫的核心環節。常規包括測試賬號管理、鑑權,腳本的配置化參數(例如視頻流壓測包括是否開啓視頻轉碼、白名單、長短連接等),接口參數構造(直接跟業務邏輯強相關),固定化的數據會導致壓測失真。因此雲壓測提供了線上流量錄製轉用例的能力,線上錄製二進制包基於協議進行協議轉換爲雲壓測支持的流量存檔格式,在123容器上的實施自動化程度較高。同時也支持CSV文件上傳,支持腳本按照列名讀取,多個CSV文件會進行merge,默認採用行數較多的作爲基準文件,從頭到尾輪詢讀取數據處理。

圖片

在大容量、高併發場景下,如何保證整體的壓測集羣發送的流量是均勻且分散的呢?雲壓測提供了按照發壓機進行切片的配置選項,默認會按照任務配置的pod數量進行文件分割,這樣保證分配到每個發壓機的測試數據不重複,引擎按照輪詢機制讀取數據,儘量保證不進行數據深拷貝,避免內存full gc導致抖動。

2.4 壓測報表分析

壓測報表分析是整個壓測流程最具有價值的一環。爲了讓用戶更加聚焦報表,雲壓測針對不同引擎、腳本提供了一致性的觀測體驗。基於opentelemetry標準來實現整體壓測報表的數據透視,包括metrics、trace、log。

圖片

首先通過metrics來判斷客戶端(壓測報表)、服務端(APM監控框架)的接口成功率、時延、服務的飽和度是否符合預期,最佳方案是針對特殊的自定義狀態碼支持業務單獨斷言。定位到異常的metrics數據,根據錯誤碼篩選請求日誌查看完整日誌,雲壓測基於http har進行gRPC等私有協議字段存檔格式兼容,保證所有引擎的日誌觀測體驗一致。針對異常的日誌請求數據,自定義traceID埋點進行流量染色,結合服務端本身的trace監控能力進行鏈路耗時排查。

圖片

採樣策略符合以下的特性:

  • 合理選用gauge、counter、histogram(分bucket) 優化指標聚合效率;

  • log配合trace設置採樣,保證腳本執行日誌鏈路完整;

  • trace&log默認設置合理採樣比例,減少日誌上報帶來性能損耗。

圖片

業務自定義檢查點,用於業務字段(例如業務特有bizCode等)斷言:

圖片

基於har存檔日誌進行請求日誌採樣,優先進行異常請求採樣、按照請求比例(默認千千分之一)。按照trace鏈路進行採樣,保證請求上下文完整性,用戶可自定義腳本中的traceid(支持Trace Context)配合業務的APM進行異常鏈路耗時定位。

圖片

用戶日誌、引擎日誌打印輸出,方便腳本調試、引擎問題排查定位。該日誌採樣策略有一定的頻率限制,避免消耗過多的cpu資源,同時可以減少日誌存儲成本。從壓測過程具備完整metrics、trace、log可觀測性鏈路數據,針對一些bad case也有完整的排查路徑。

圖片

03、實踐案例

3.1 手Q春保活動

3.1.1 背景

目前手Q業務每年元旦和春節的0點0分,都會有一波非常高的尖峯。讀鏈路和寫鏈路分不同命令字會數倍地流量飆升。業務架構保障高可用,大部分都是多地部署的,針對只讀鏈路可以通過集羣流量調度做到讀鏈路的壓測。但是寫鏈路的壓測(主要包含 feed 發表、評論、點贊等交互)較缺乏,測試數據構造複雜,用例編寫成本高。雲壓測之前主要採用JS腳本進行場景編排,圖文場景出現了內存佔用高,二進制頻繁進行數據深拷貝,給壓測機內存資源準備帶來不少的壓力。

3.1.2 實施方案

接下來講實施方案。2023年的春保採用Go Plugin進行用例重構,複用了現有的數據編解碼組件能力,極大降低了適配成本,並且由於是原生go的協程調度執行,減少了大字符、字節碼變量轉換帶來的性能損耗,同時也避免內存 gc 帶來的流量抖動。支持複用存量的協議封裝、鑑權接口,無需單獨維護JS引擎的轉換成本。相同分片上傳的接口場景下,採用Go Plugin腳本相比JS腳本的 1000 併發吞吐量提升了90%的性能表現,有效的降低業務的硬件資源使用成本。

圖片

3.1.3 業務落地效果

  • 在6-8倍的日常流量保障目標下,探測上下游的服務過載情況,提前進行擴容儘早干預;

  • 針對鏈路超時的現象,合理設置重試策略、超時時間,驗證柔性策略是否生效,避免瞬時壓力造成雪崩效應;

  • 支持上海、南京、廣州等多個地域集羣壓測,最高達到10w併發數,目標RPS達到100w的吞吐量規模,支持100G級別帶寬流量驗證。

3.2 視頻業務容災演習

3.2.1 背景

演習和壓測是首頁鏈路各個服務重構後的一次整體摸底,也是2023年春節保障的提前演習。這裏主要做3件事:

  • 容災演習:爲了驗證首頁接口在各種異常情況下的容災容錯能力,梳理容災容錯短板;

  • 壓測:爲了排查首頁鏈路中的各個關鍵服務性能是否存在問題,找到鏈路性能瓶頸,明確鏈路服務擴容模型;

  • 接入層兜底能力摸底,當首頁接口故障情況下,兜底能力能否達到預期的目標

3.2.2 實施方案

根據演習計劃,關注告警信息、容器負載、主被調、失敗率、平均耗時等指標。從接入層通過透傳流量標識進行壓測流量打標,上下游鏈路涉及RPC調用,緩存中間件、數據庫中間件、消息中間件等,整體框架需要接入統一治理服務,保障數據隔離、服務隔離。壓測過程中,雲壓測集成了被壓測服務的SLA監控,根據服務的重要程度進行等級劃分,並且通過告警收斂自動進行流量降級、熔斷,特性環境壓測有效的減少規避事故的爆炸範圍。長時間的持續高負載也會帶來服務的潛在的雪崩效應,如何兼顧壓測流量的飽和度和業務流量安全,是需要持續進行策略迭代優化。

圖片

服務容災驗證路徑概述如下:

  • 混沌工程注入,驗證接口的健壯性;

  • 兜底緩存策略的觸發機制;

  • 驗證服務降級、熔斷策略的機制;

  • 驗證服務的過載保護能力,具備柔性可用;

  • 校驗業務安全管控策略、高可用。

3.2.3 業務落地成果

  • 驗證業務的彈性伸縮能力,對降級、熔斷、柔性服務進行可用性驗證;

  • 通過 PTS 提供的 RPS 擴散模型,瞭解上下游服務的機器規格配比,爲容量預估、機器擴容、縮容做好評估依據。

04、總結展望

雲壓測主要聚焦在壓測平臺的發壓端基礎能力構建,實時展示了客戶端的性能指標趨勢,包括併發數、RPS、latency、錯誤率,適配大部分HTTP、gRPC、websocket等協議,針對私有協議、信息流、視頻流有go plugin的自定義埋點解決方案。目前對服務端的監控數據整合相對欠缺,用戶需要頻繁切換多個監控平臺來觀測服務的負載,針對各業務常用 APM 集成方案在規劃迭代中。平臺後續會圍繞着性能測試自動化、智能化的目標會持續迭代。儘量減少用戶手動操作的成本,通過相對自動化的解決方案來去定吞吐量、檢測系統性能瓶頸,並且基於SLA標準進行流量降級、熔斷能夠有效的保障壓測安全。

圖片

以上是雲壓測百萬級 QPS 壓測解決方案和在它手Q、在線視頻業務實踐的全部內容,歡迎各位讀者在評論區分享交流。

-End-

原創作者|張澤強

技術責編|張澤強、劉楚蓉

你可能感興趣的騰訊工程師作品

| 騰訊雲開發者2022年度熱文盤點

| 企業微信零耦合集成騰訊會議和騰訊文檔插件化架構實踐

| 7天DAU超億級,《羊了個羊》技術架構升級實戰

國民級應用:微信是如何防止崩潰的?

技術盲盒:前端後端AI與算法運維|工程師文化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章