全鏈路仿真壓測系統

1. 項目背景

目前常用的壓測工具一般都是針對 QPS 這一個單一指標進行考量。即使支持編寫腳本的工具也只是通過參數化模擬用戶。但是實際用戶是使用單獨設備請求服務器,即一個用戶就是一個 tcp 連接。

所以爲了更真實的模擬用戶行爲,我們需要通過一個 tcp 連接模擬一個用戶,並通過代碼方式實現用戶的真實請求行爲。C 端及中臺產研中心雲平臺部質量保障團隊自研的 “仿真壓測系統”。獨有的 QPS 動態可控技術,支持固定 URL 壓測、參數化、Websocket 協議壓測、中間件、數據庫等的壓測,模擬用戶真實軌跡,通過用戶側,服務端,DB 進行數據一致性和正確性自動檢驗,打造真正的全鏈路仿真壓測,該系統可擴展性強,穩定性高,目前已多次支持公司級各類大型活動的仿真全鏈路壓測,爲線上服務的穩定運行提供了強有力的質量保障。

經過四年的打磨如今藉此機會與大家分享一下心路歷程,積極討論,歡迎提出意見,希望可以幫助我們進一步成長。(文中引用均爲測試數據)

2. 仿真壓測系統成型之路

2.1

工具選型

► 2.1.1 現有的壓測工具對比                 

圖片

圖 1-1

► 2.1.2  Locust 發壓 client 比較

圖片

圖 1-2

結合以上比較結果,目前沒有合適的壓測工具可以直接滿足我們的壓測需求,最終決定用 Locust 做框架, 首先它是一款易於使用的分佈式負載測試工具,完全基於事件,單機併發能力高,併發機制是協程,其次二次開發潛力比較大。

發壓的請求 client 採用 fasthttp, 據說是目前 golang 性能最好的 http 庫,原理是利用 worker 複用 goroutine,減輕 runtime 調度協程的壓力,相對於自帶的 net/http(每個連接都需要新建一個 goroutine),性能有 10 倍的提升。省略一千字,直接上乾貨。

► 2.1.3 仿真壓測系統特性:

……

► 2.1. 4 實現難點分析

模擬百萬用戶創建 TCP/ websocket 連接;長時間保持連接不斷開;集中發壓;

部分代碼如下:

   go func() {
    for {
        select
        case data := <-r.stats.messageToRunnerChan:
            data["user_count"] = r.numClients
            r.outputOnEevent(data)
        case <-r.closeChan:
            Events.Publish("boomer:quit")
            r.stop()
            wg.Done()
            return
        }
    }
}()
集中發壓
curTime := myStatus.Ct
    statusTime := myStatus.St
    c := time.After(time.Duration(statusTime-curTime) * time.Second)
    <-c
    miaosha(uidToken, &myStatus, myLog)
保持長鏈接
func wsWorker() {
  ws, err := websocket.Dial(wsUrl, "", origin)
  if err != nil {
  log.Println(err)
  return
  }
  defer func() {
    if p := recover(); p != nil {
    log.Println(p)
 }
 erc := ws.Close()
    if erc != nil {
     log.Println(erc)
  }
  }()
  go func() {
    ws.Write([]byte("0"))
    var t = time.Tick(time.Second * 30)
    for range t {
    if ws == nil {
        continue
    }
    ws.Write([]byte("0"))
    fmt.Println("send 0 to websocket ok!")
    }
    }()
  for {
    select {
      case <-stopChannel:
           return
      default:
        var msg = make([]byte, 512)
        start := time.Now()
        n, err := ws.Read(msg)
        log.Println(string(msg[0:n]))
        elapsed := time.Since(start)
        if myDebug {
            log.Println(string(msg))
           }
        if err != nil {
          log.Printf("Error: %s", err.Error())
        }
       }
     }
    }

2.2

日常使用截圖

圖片

圖 2 - 平臺首頁

圖片

圖 3 - 實時監控壓測頁面,總請求數、實時 qps、響應時間、發壓機 cpu 佔用等指標。

圖片

圖 4 - 壓測空接口,服務器監控截圖, Nginx 的峯值 QPS 可以達到 596 萬。

圖片

圖 5 - 壓測 redis 時監控截圖,單個節點的實時監控等都可以一覽無餘

3. 818 臺網實戰開始

工欲善其事,必先利其器,利器已有,劇本安排上。

晚會當天數以萬計的用戶通過手機 app 參與活動,從白天開始 “預約紅包”,晚上八點晚會開始後用戶開始陸陸續續登錄 app“領取預約紅包”;隨着主持人口播,第一輪“集卡” 活動開始…… 人數慢慢增多,到了最激動人心的時候。“一元秒新車”開始,QPS 狂飆,TCP 連接數也達到了峯值 100W。

開始實戰:

控制檯:各部門注意,入口已經放開,流量開始進入;目前 “預約紅包” 階段

仿真壓測:收到,每五分鐘遞增 1w 用戶(1TCP 模擬 1 用戶以下通用 TCP 數代指用戶數),QPS200(用戶進入活動頁面,預約紅包、查看規則、查看介紹等大概每人 5 次請求)

圖片

圖 6 - 由於發壓機數量 32 臺,每個機器限制 QPS7 所以總 QPS 就是 224

控制檯:晚會開始,“預約紅包” 可領狀態,“見面禮紅包” 已開啓,“連續紅包” 已開啓,第一場大咖講話馬上開啓……

仿真壓測:明白,TCP 調整到 20W,QPS2W(用戶操作 5 次集中在 1 分鐘以內)

圖片

圖 7 - 動態調整 QPS、TCP 連接數按鈕

圖片

圖 8 - 設置 TCP 連接數、QPS、步增等

圖片

圖 9 - 設置生效,TCP、QPS 穩定保持在設定值

控制檯:抽盲盒活動進入倒計時

仿真壓測:copy,TCP 調整到 30W,QPS10W(用戶 3 秒內點擊 10~15 次提交一次請求到服務器)

圖片

圖 10 - 設置生效,TCP、QPS 穩定保持在設定值

控制檯:時間來到最終環節,“一元秒新車”,倒計時開啓

仿真壓測:拼手速的時候到了,TCP100W,QPS100W(秒殺只可以提交一次)走起

圖片

圖 11 - 設置生效,TCP、QPS 穩定保持在設定值

至此,晚會結束,仿真壓測卻並沒有結束,還有最後一環 --“對賬”。仿真壓測會把接口返回用戶的數據記錄到 kafka 中,最後入庫,與服務器的數據進行對賬,確保每條數據的正確性。

圖片

圖 12 - 服務側數據與用戶側數據進行對賬

看一下動態調整 QPS 的完美曲線, 隨着用戶增長以及 QPS 增長的仿真圖

圖片

圖 13 - 仿真模擬用戶遞增的曲線

圖片

圖 14 - 服務器監控截圖,仿真用戶全部走 websocket 協議,通過 “狀態機” 下發指令、接收指令

圖片

 圖 15-Redis 監控截圖

圖片

圖 16 - 雲服務器 LB 截圖

圖片

圖 17 - 雲服務器 LB 截圖

圖片

圖 18 - 雲服務器 LB 截圖

圖片

圖 19 - 仿真壓測完成,服務側獎品發放明細

圖片

圖 20 - 仿真側用戶領取獎品明細以及對賬情況

數據完美對上。

4. 總結

由於篇幅受限,想說的很多,質量保障團隊也做了很多,比如災備演練,模擬服務器滿載、域名切換、服務器宕機、機房切換等等。關於測試方面的問題歡迎私下討論。

作者簡介

劉建宇

■ 雲平臺部 - 質量保障團隊。

■  2017 年 10 月份加入汽車之家,一邊支持業務測試一邊從測試角度出發,爲測試提供更好用的測試工具;參與並主導了四年 818 臺網互動的測試工作。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/RzAviTBUsemb4vjNTVNidg