一、前言
有贊致力於成為商家服務領域裡最被信任的引領者,因為被信任,所有我們更需要為商家保駕護航,保障系統的穩定性。有贊從去年開始通過全鏈路壓測,模擬大促真實流量,串聯線上全部系統,讓核心系統同時達到流量峰值:
- 驗證大促峰值流量下系統穩定性
- 容量規劃
- 進行強弱依賴的劃分
- 降級、報警、容災、限流等演練
- ...
通過全鏈路壓測這一手段,對線上系統進行最真實的大促演練,獲取系統在大壓力時的表現情況,進而準確評估線上整個系統叢集的效能和容量水平,不辜負百萬商家的信任。
有贊對於效能測試主要有線下單系統單介面、線上單系統以及線上全鏈路壓測等手段,通過不同維度和顆粒度對介面、系統、叢集層面進行效能測試,最終保障系統的穩定性。這裡主要講述一下,有贊全鏈路壓測的相關設計和具體的實施。
二、整體設計
說到全鏈路壓測,業內的淘寶、京東都都已有很成熟的技術,主要就是壓測流量的製造、壓測資料的構造、壓測流量的識別以及壓測資料流向的處理;直接看下有贊壓測的整體設計:
- 大流量下發器:其實就是模擬海量的使用者去使用我們的系統,提供壓測的流量,產生大促時的場景和流量;
- 資料工廠:構造壓測鏈路中使用者請求的資料,以及壓測鋪底的資料、資料清洗、脫敏等工作;
壓測平臺負責管理壓測指令碼和壓測請求資料,從資料工廠獲取壓測資料集,分發到每一個壓測 agent 機器上,agent 根據壓測指令碼和壓力目標對線上機器發起請求流量,模擬使用者的檢視商品-新增購物車-下單-支付等行為,線上服務叢集識別出壓測的流量,對於儲存的讀寫走影子儲存。這裡就要說到,線上壓測很重要的一點就是不能汙染線上的資料,不能讓買賣家有感知,比如壓測後,賣家發現多了好多訂單,買家發現錢少了。所以,線上服務需要能夠隔離出壓測的流量,儲存也需要識別出壓測的資料,下面看一下有讚的壓測流量和壓測資料儲存的隔離方案。
三、流量識別
要想壓測的流量和資料不影響線上真實的生產資料,就需要線上的叢集能識別出壓測的流量,只要能識別出壓測請求的流量,那麼流量觸發的讀寫操作就很好統一去做隔離了,先看一下有贊壓測流量的識別:
3.1 同步請求
全鏈路壓測發起的都是Http的請求,只需要要請求頭上新增統一的壓測請求頭,具體表現形式為: Header Name:X-Service-Chain; Header Value:{"zan_test": true}
Dubbo協議的服務呼叫,通過隱式引數在服務消費方和提供方進行引數的隱式傳遞,表現形式為: Attachments:X-Service-Chain; Attachments Value:{"zan_test": true}
通過在請求協議中新增壓測請求的標識,在不同服務的相互呼叫時,一路透傳下去,這樣每一個服務都能識別出壓測的請求流量,這樣做的好處是與業務完全的解耦,只需要應用框架進行感知,對業務方程式碼無侵入
3.2 中介軟體
- NSQ:通過 NSQMessage 中新增 jsonExtHeader 的 KV 擴充資訊,讓訊息可以從 Producer 透傳到 Consumer 端,具體表現形式為:Key:zan_test;Value:true
- Wagon:對來自影子庫的 binlog 通過擴充訊息命令(PUB_EXT)帶上壓測標記{"zan_test": true}
3.3 非同步執行緒
非同步呼叫標識透傳問題,可以自行定製 Runnable 或者定製執行緒池再或者業務方自行定製實現。
四、資料隔離
通過流量識別的改造,各個服務都已經能夠識別出壓測的請求流量了,那麼如何做到壓測資料不汙染線上資料,對於不同的儲存做到壓測資料和真實的隔離呢,有贊主要有客戶端 Client 和 Proxy 訪問代理的方式實現,下面就看一下有讚的資料隔離方案:
4.1 Proxy 訪問代理隔離
針對業務方和資料儲存服務間已有Proxy代理的情況,可以直接升級 Proxy 層,儲存使用方完全無感知,無侵入,下面以 MySQL 為例,說明 Proxy 訪問代理對於壓測資料隔離的方案;
業務方應用讀寫DB時,統一與 RDS-Proxy (介於 MySQL 伺服器與 MySQLClient 之間的中介軟體)互動,呼叫 RDS-Proxy 時會透傳壓測的標記,RDS 識別出壓測請求後,讀寫 DB 表時,自動替換成對應的影子表,達到壓測資料和真實的生產資料隔離的目的
ElasticSearch、KV 對於壓測的支援也是通過 Proxy 訪問代理的方式實現的
4.2 客戶端SDK隔離
業務應用通過Client呼叫儲存服務時,Client 會識別出壓測的流量,將需要讀寫的 Table 自動替換為影子表,這樣就可以達到影子流量,讀寫到影子儲存的目的;
Hbase、Redis 等採用此方案實現
4.3 資料偏移隔離
推動框架、中介軟體升級、業務方改造,難免會有遺漏之處,所以有贊對於壓測的資料統一做了偏移,確保買賣家 ID 與線上已有資料隔離開,這樣即使壓測資料由於某種原因寫入了真實的生產庫,也不會影響到線上買賣家相關的資料,讓使用者無感知;
這裡要說一下特殊的週期掃表應用的改造,週期性掃表應用由於沒有外部觸發,所有無法掃描影子表的資料,如何讓這樣的 job 叢集整體來看既掃描生產庫,也掃描影子庫呢? 有讚的解決思路是,部署一些新的 job 例項,它們只掃描影子庫,訊息處理邏輯不變;老的 job 例項保持不變(只掃生產庫)
五、壓測平臺
有讚的全鏈路壓測平臺目前主要負責壓測指令碼管理、壓測資料集管理、壓測 job 管理和排程等,後續會有重點介紹,這裡不做深入
壓測的“硬體”設施基本已經齊全,下面介紹一下有贊全鏈路壓測的具體實施流程吧
六、壓測實施流程
廢話不多說,直接上圖:
有贊全鏈路壓測的執行流程如上圖所示,下面具體看一下幾個核心步驟在有贊是怎麼做的。
6.1 壓測計劃制定
要想模擬大促時線上真實的流量情況,首先需要確認的就是壓測場景、鏈路,壓測的目錄,以及壓測的流量漏斗模型:
- 壓測的鏈路:根據公司具體業務具體分析,有贊屬於電商型別公司,大促時候的峰值流量基本都是由於買家的購買行為導致的,從巨集觀上看,這樣的購買行為主要是店鋪首頁-商品詳情頁-下單-支付-支付成功,我們把這一條骨幹的鏈路稱為核心鏈路,其實大促時主要就是保證核心鏈路的穩定性
- 壓測鏈路的漏斗模型:線上真實的場景案例是,100個人進入了商家的店鋪首頁,可能有50個人直接退出了,有50個人最終點選進入了商品詳情頁面,最終只有10個人下了單,5個人真正付款了,這就是壓測鏈路的漏斗模型,也就是不同的介面的真實呼叫比例;實際的模型制定會根據近7天線上真實使用者的行為資料分型分析建模,以及往期同型別活動線上的流量分佈情況,構建壓測鏈路的漏斗模型
- 壓測的場景:根據運營報備的商家大促活動的計劃,制定大促的壓測場景(比如秒殺、抽獎等),再結合近七天線上流量的場景情況,綜合確定壓測的場景;
- 壓測目標:根據運營報備的商家大促預估的PV和轉換率情況,結合去年同期線上流量情況和公司業務的增長速率,取大值作為壓測的目標
6.2 資料工廠
前面我們已經介紹瞭如何確定壓測的目標、場景、鏈路,那麼壓測的資料怎麼來尼,這就是資料工廠登場的時候了,下面就介紹一下有贊壓測的資料工廠
有讚的壓測資料工廠主要負責,壓測鋪底資料的準備、壓測請求資料塊的生成;
6.3 鋪底資料準備
壓測準備鋪底的資料,這個眾所周知的,但是由於有贊壓測的方案採用的是影子庫的設計,所以對於鋪底資料準備不得不去處理影子庫的資料。直接看下鋪底資料準備的流程圖:
- 資料匯入 從生產資料庫按需過濾,匯入壓測鋪底需要的資料到大資料叢集的hive表中。
- 資料處理 在 hive 表中,對匯入的資料進行脫敏和清洗,清洗的目的是保證壓測的資料可用性,比如保證鋪底商品庫存最大、營銷活動時間無限、店鋪正常營業等。
- 資料匯出 對 hive 標中已經處理完成的資料,匯出到已經建立好的影子庫中,需要注意的是同一例項寫入資料的控制(因為影子庫和生產庫同例項),寫入資料的 binlog 過濾。
有讚的壓測資料準備目前全部在 DP 大資料平臺上操作,基本完成了資料準備操作的自動化,維護了近千的資料準備 job
壓測請求資料資料集
gatling 原生支援 json、csv、DB 等方式的資料來源載入,我們採用的壓測資料來源是 json 格式的,那麼如此海量的壓測源資料,是通過什麼方式生成和儲存的尼,我們的實現還是依託於 DP 大資料平臺,通過 MapReduce 任務的方式,按需生成我們壓測請求需要的資料集:
- 從各個業務線的表中獲取壓測場景整個鏈路所以介面請求需要的引數欄位,存到一張建立好的壓測資料來源寬表中
- 編寫 MapReduce 任務程式碼,讀取壓測資料來源寬表資料,按壓測的介面請求引數情況,生成目標 json 格式的壓測請求資料塊檔案到 HDFS
- 壓測時,壓測引擎自動從 HDFS 上拉取壓測的請求資料塊
MapReduce 生成的資料集 json 示例:
6.4 壓測指令碼準備
6.4.1 梳理壓測請求和引數
壓測就要知道壓測的具體介面和介面引數,所以我們採用統一的 RESTful 風格規範,讓各個業務方的人員提交壓測介面的 API 文件,這樣壓測指令碼編寫人員就能根據這份 API 快速寫出壓測的指令碼,以及介面的預期結果等6.4.2 控制漏斗轉化率
有讚的壓測引擎用的是公司二次封裝的 gatling,原生就支援漏斗比例的控制,直接看例子
6.4.3 不同場景的配比
setUp(
scn0.inject(constantUsersPerSec(10) during (1 minute)).throttle(
reachRps(300) in (30 seconds),
holdFor(2 minute)).protocols(CustomHttpProtocol.httpProtocol),
scn1.inject(constantUsersPerSec(10) during (1 minute)).throttle(
reachRps(500) in (10 seconds),
holdFor(3 minute)).protocols(CustomHttpProtocol.httpProtocol),
scn2.inject(constantUsersPerSec(10) during (1 minute)).throttle(
reachRps(200) in (20 seconds),
holdFor(1 minute)).protocols(CustomHttpProtocol.httpProtocol)
)
複製程式碼
對不同的壓測場景鏈路按模組編寫壓測指令碼,這樣的好處就是需要不同場景混合壓測時,只需要在 setUp 時,按需把不同的場景組合到一起即可;需要單獨壓測某一個場景時,setUp 中只留一個場景就好,做到一次編寫,處處可壓。
6.5 壓測執行
壓測的鋪底資料、壓測指令碼、壓測請求的資料集都已經介紹完了,可謂是萬事俱備只欠東風,那這個東風就是我們自建的壓測平臺 maxim,這裡不對壓測平臺的設計展開介紹,展示一下 maxim 在壓測執行過程中所承擔的工作。
maxim平臺主要的功能模組有:
- 測試指令碼:負責測試指令碼的管理
- 資料集:負責壓測請求資料集的管理,目前主要有兩種資料集上傳模式:直接上傳資料包和 hadoop 叢集資料來源路徑上傳。第二種上傳模式,只需要填寫資料來源所在的 hadoop 叢集的路徑,maxim 平臺會自動去所在路徑獲取壓測資料集檔案
- 測試 job:負責測試任務的管理,指定壓測 job 的指令碼和指令碼入口,以及壓測資料集等
- 壓測注入器:負責展示壓測注入機器的相關資訊
- 壓測報告生成:壓測報告的生成,直接用的 gatling 原生的報告生成功能
maxim 平臺壓測結果報告
下面看一下壓測執行的頁面功能:
- 壓力注入器數量:指定本次壓測執行,需要多少臺壓測機去執行
- 重複場景測試:一個虛擬使用者重複幾次壓測場景
- 併發使用者數:可執行壓測時,按需填寫需要的每秒載入的併發使用者數和持續時間,無需每次變更壓測指令碼
- 目標 RPS:可執行壓測時,按需填寫壓測的目標 RPS,爬坡時間,目標持續時間,達到限流的作用,可同時指定多個目標 RPS,達到分梯度壓測的目的;
七、最後
到這裡有贊全鏈路壓測方案已經介紹完了,因為篇幅的原因還有很多實施細節部分並沒有完整表述,同時有讚的全鏈路壓測也才初具雛形,歡迎有興趣的同學聯絡我們一起探討,有表述錯誤的地方也歡迎大家聯絡我們糾正。