結論先講

壓測平台的核心價值不是「跑壓測」,而是「讓壓測結果可被重複、可被比較、可被自動分析」。 用 CLI 跑一次 k6 很容易,但要比較 9 個框架 × 7 種 VU × 3 種場景的數據,你需要一個平台。


為什麼不直接用 k6 CLI

k6 本身是一個很好的壓測工具。你可以寫一個腳本、命令列跑一次、看 stdout 的摘要。對於「測一下這個 API 撐不撐得住」的場景,這就夠了。

但當需求變成:

  • 比較 9 個後端框架在同一場景下的表現
  • 每個框架跑 7 種 VU(10, 50, 100, 500, 1K, 5K, 10K)
  • 每次跑完自動判斷瓶頸在哪裡
  • 結果要能回溯、能比較、能產報告

光靠 k6 CLI 就不夠了。你需要:

  1. 腳本管理: 版本化、可編輯、可重複使用
  2. 指標儲存: 即時寫入時序資料庫,不是跑完看一次就丟掉
  3. 視覺化: 不是看數字,是看趨勢
  4. 自動分析: 20 條規則掃過去,不是人眼盯著數字猜

整體架構

┌─────────────────────────────────────────────────┐
│                  Web UI (Next.js 15)             │
│              port 13000                          │
│  ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│  │ Scenario │ │ Monaco   │ │ Results Viewer   │ │
│  │ Manager  │ │ Editor   │ │ + Comparison     │ │
│  └──────────┘ └──────────┘ └──────────────────┘ │
└─────────────────┬───────────────────────────────┘
                  │ REST API + WebSocket
┌─────────────────▼───────────────────────────────┐
│              API Server (Express + TS)           │
│              port 14000                          │
│  ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│  │ Scenario │ │ k6       │ │ Rule Engine      │ │
│  │ CRUD     │ │ Runner   │ │ (20 rules)       │ │
│  └──────────┘ └──────────┘ └──────────────────┘ │
└───┬──────────────┬──────────────┬───────────────┘
    │              │              │
    ▼              ▼              ▼
┌────────┐  ┌──────────┐  ┌──────────────┐
│ PG 16  │  │ InfluxDB │  │ Grafana 11   │
│ Config │  │ Metrics  │  │ Dashboard    │
│ Reports│  │ (k6→xk6) │  │ port 13001   │
└────────┘  └──────────┘  └──────────────┘
                │
                ▼
         ┌──────────┐
         │ Target   │
         │ API      │
         │ (被測系統) │
         └──────────┘

每一層都有明確的職責,接下來逐層說明。


層次一:壓測引擎(k6 + xk6-influxdb)

為什麼選 k6

壓測工具的選擇不少:JMeter、Gatling、Locust、k6、Artillery。選 k6 的理由:

工具語言資源消耗腳本可讀性即時輸出
JMeterXML + GUI高(JVM)需外掛
GatlingScala DSL中(JVM)
LocustPython
k6JavaScript低(Go runtime)
ArtilleryYAML + JS中(Node)

k6 用 Go 寫的 runtime 執行 JavaScript 腳本,資源消耗遠低於 JVM 系列。腳本就是 JS,前後端工程師都看得懂。而且它的 stages 設計讓 VU 的逐步增加非常直覺:

export const options = {
  stages: [
    { duration: '1m', target: 50 },   // 1 分鐘內爬到 50 VU
    { duration: '3m', target: 50 },   // 維持 50 VU 跑 3 分鐘
    { duration: '1m', target: 100 },  // 1 分鐘內爬到 100 VU
    { duration: '3m', target: 100 },  // 維持 100 VU 跑 3 分鐘
    { duration: '1m', target: 0 },    // 收尾
  ],
};

xk6-influxdb:即時指標輸出

k6 預設把結果輸出到 stdout。用 xk6-influxdb 擴展,讓 k6 在壓測進行中即時把每個請求的指標寫進 InfluxDB:

  • http_req_duration: 每個請求的 response time
  • http_req_failed: 是否失敗
  • http_reqs: 請求計數(算 RPS 用)
  • vus: 當前虛擬用戶數

這些指標按秒寫入 InfluxDB,Grafana 可以即時畫圖。壓測還在跑,你就能看到系統正在崩潰。


層次二:指標儲存(InfluxDB v2)

為什麼用時序資料庫

壓測數據本質上是時序數據:每秒的 RPS、每秒的 P95 latency、每秒的錯誤率。用 PostgreSQL 也能存,但查詢效率差很多。

InfluxDB 的優勢:

  • 時間視窗聚合: 「過去 5 分鐘的 P95 response time」一句 Flux query 搞定
  • 自動降採樣: 保留最近 7 天的秒級數據,更早的自動聚合成分鐘級
  • Grafana 原生整合: 不需要寫 adapter

資料保留策略

秒級數據 → 保留 7 天
分鐘級數據 → 保留 30 天
報告摘要 → 永久保留(存 PostgreSQL)

不需要永久保留每秒的數據。分析完的結論存 PostgreSQL,原始數據可以清掉。


層次三:視覺化(Grafana 11)

為什麼不自己畫圖

因為 Grafana 已經把壓測視覺化做到很好了。自己用 Recharts 或 D3 畫,要花很多時間處理:

  • 時間軸對齊
  • 多指標疊圖
  • 動態縮放
  • 資料來源切換

Grafana 一個 dashboard 就搞定。我們的 dashboard 包含:

  1. VU 與 RPS 趨勢: 左軸 VU、右軸 RPS,看吞吐量跟不跟得上用戶數
  2. Response Time 分位數: P50、P90、P95、P99 四條線,看 tail latency
  3. Error Rate: 錯誤率趨勢,和 VU 疊在一起看「什麼時候開始壞」
  4. HTTP Status 分佈: 200/400/500 的比例,判斷是 client error 還是 server error

但 Web UI 還是需要自己做

Grafana 負責「看即時數據」。但壓測平台還需要:

  • 場景管理(建立、編輯、排程)
  • 腳本編輯(Monaco Editor)
  • 歷史結果比較(跨框架、跨 VU)
  • 規則引擎分析結果

這些是 Next.js Web UI 的職責。


層次四:規則引擎

為什麼要自動分析

一次壓測跑完,你會得到:

  • 每秒的 RPS
  • 每個請求的 response time(P50/P90/P95/P99)
  • 錯誤率
  • 各 HTTP status code 的數量

要判斷「系統在哪裡開始出問題」「瓶頸是什麼」,需要把這些數字交叉比對。人可以做,但當你有 9 個框架 × 7 種 VU = 63 份報告要分析,人工分析不現實。

20 條分析規則

規則引擎目前有 20 條規則,分成幾大類:

Response Time 規則

規則觸發條件判定
P95 過高P95 > 2000ms警告
P99 離群P99 / P50 > 10Tail latency 問題
Response time 急劇上升斜率 > 閾值系統正在崩潰

Throughput 規則

規則觸發條件判定
RPS 飽和VU 加倍但 RPS 不變吞吐量瓶頸
RPS 下降RPS 比前一階段低系統過載

Error 規則

規則觸發條件判定
錯誤率閾值error rate > 1%穩定性問題
錯誤率飆升error rate 斜率突增系統正在崩潰
5xx 比例5xx / total error > 50%Server-side 問題

瓶頸歸因規則

規則判斷依據歸因
CPU 阻塞高 P95 + 低 RPS + 無 DB 等待CPU-bound(如 bcrypt)
DB 連線池錯誤訊息含 pool/timeout連線池不足
記憶體壓力response time 隨時間線性上升Memory leak

規則引擎的設計原則

interface Rule {
  id: string;
  name: string;
  category: 'response_time' | 'throughput' | 'error' | 'bottleneck';
  evaluate: (metrics: BenchmarkMetrics) => RuleResult;
  severity: 'info' | 'warning' | 'critical';
}
 
interface RuleResult {
  triggered: boolean;
  message: string;
  evidence: Record<string, number>;  // 佐證數據
  suggestion: string;                 // 修復建議
}

每條規則獨立運作,吃同一份 metrics 資料,產出結構化的結果。規則之間不互相依賴——這讓規則的新增和修改很安全。


層次五:資料管理(PostgreSQL 16)

InfluxDB 存即時指標,PostgreSQL 存其他所有東西:

  • Scenario 設定: 測試場景的名稱、VU 設定、目標 API、k6 腳本
  • Run 記錄: 每次跑的時間、狀態、結果摘要
  • Rule 分析結果: 每次跑完規則引擎的分析
  • Framework 設定: 各框架的 port、health check URL、Docker config

用 Sequelize ORM,schema migration 管理資料庫版本。


為什麼全部 Docker Compose 化

整個平台用 Docker Compose 管理:

services:
  web:        # Next.js Web UI
  api:        # Express API Server
  postgres:   # 設定/報告儲存
  influxdb:   # k6 指標
  grafana:    # 視覺化
  # --- 被測系統 ---
  express-ts: # Framework 1
  django:     # Framework 2
  fastapi:    # Framework 3
  # ... 其他 6 個框架
  mysql:      # 被測系統的資料庫
  redis:      # 被測系統的快取

Docker Compose 化的好處:

  1. 環境一致: 不管在 Mac 還是 Linux 跑,環境一模一樣
  2. 資源可控: 可以限制每個框架的 CPU 和 RAM(壓測公平性的關鍵,下一篇會詳細講)
  3. 一鍵啟動: docker compose up -d 就能開始測
  4. 互相隔離: 框架之間不會搶資源

資料流:從點擊「Run」到看報告

  1. 用戶在 Web UI 選場景、選框架、點 Run
  2. Web UI 呼叫 API Server 的 /runs endpoint
  3. API Server 產生 k6 腳本(注入 VU、duration、target URL)
  4. API Server spawn k6 process,k6 開始打被測系統
  5. k6 即時把指標寫入 InfluxDB(透過 xk6-influxdb)
  6. API Server 透過 WebSocket 把進度推給 Web UI
  7. Grafana 同時從 InfluxDB 拉數據,即時更新 dashboard
  8. k6 跑完,API Server 從 InfluxDB 拉最終數據
  9. 規則引擎跑 20 條規則,產出分析報告
  10. 結果存 PostgreSQL,Web UI 顯示報告

步驟 9 是關鍵。人不需要盯著 Grafana 看「系統什麼時候開始壞」——規則引擎會告訴你。


這個架構的取捨

選了什麼

  • InfluxDB v2 而非 Prometheus: k6 原生支援 InfluxDB output,不需要額外的 exporter
  • PostgreSQL 而非 SQLite: 多人協作時需要真正的資料庫
  • spawn process 而非 k6 library mode: process 隔離更乾淨,k6 掛了不會拖垮 API Server
  • 自建規則引擎而非用 Grafana Alert: 規則需要跨指標交叉比對,Grafana Alert 做不到

沒選什麼

  • 沒有分散式壓測: 單機就能打到框架極限,不需要 k6 cloud 或多機部署
  • 沒有 CI/CD 整合: 目前是研究用途,不是跑在 pipeline 裡
  • 沒有用戶認證: 內部工具,不需要

下一篇

控制變因的藝術 — 當你要比較 9 個後端框架的效能,「公平」比「快」更重要。怎麼用 Docker 資源限制、統一 DB 配置、統一 hash 算法來確保比較基準一致。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:為什麼需要 API 壓力測試?從「能動」到「能撐」 → 下一篇:控制變因的藝術:跨框架壓測怎麼做到公平