使用影分身之術,挑戰多任務負載測試
前言
回到昨天的題目,我們只模擬尖峰時段,大量的購物車加入商的測試場景,但實際運行環境上,可能會不同的使用者做著不同的事情:購買商品、搜尋商品、加入到購物車、移除商品到購物車、結帳和付款等等。就像《火影忍者》裡的「影分身之術」吧!當主角鳴人同時分出數十個影分身,每個分身都在做不同的事:戰鬥、學習、探查,這就像我們的系統在面對多任務負載時的挑戰!
挑戰目標
模擬一個視訊串流平台的高負載場景,並測試系統在同時進行多任務操作(如影片上傳、即時觀看、評論、搜尋)的情況下,是否能保持系統穩定。
挑戰設計測試場景:
- 場景 1:模擬用戶大量上傳影片,並且同時進行即時觀看,測試系統的寫入和讀取性能。
- 場景 2:在用戶大量提交評論的時候,同時進行影片的搜尋,測試系統在高頻率讀寫操作中,是否能夠維持回應的速度。
- 場景 3:模擬影片上傳的瞬間尖峰,同時用戶進行多條影片的即時觀看和評論,測試系統是否能夠承受高並發操作並平穩恢復。
測試背景資訊
系統架構
graph LR
A[Load Balancer] --> B[Web Servers]
B --> C[Media Servers]
C --> D[Content Delivery Network]
B --> E[Cache Layer]
C --> F[Database Cluster]
- 負載平衡 (Load Balancer):用來幫忙分流的伺服器,用來將用戶分流到不同的 Web 伺服器。
- Web 伺服器 (Web Server):用來處理使用者請求並且轉址到影片播放服務。
- 媒體伺服器 (Media Servers):負責影片的編碼與傳輸。
- 內容傳遞網路 (Content Delivery Network):加速影片內容的傳輸,通常會分布在不同區域 (Region),以減少因為距離導致的網路延遲。
- 快取層 (Cache Layer):加速熱門影片的傳播與播放。
串流網站的 API 範例
HTTP 方法 | API 路徑 | 描述 |
---|---|---|
GET | /videos | 取得影片列表 |
GET | /videos/{videoId} | 取得指定影片的詳細資訊 |
POST | /videos | 上傳影片 |
PUT | /videos/{videoId} | 更新指定影片的資訊 |
DELETE | /videos/{videoId} | 刪除指定影片 |
GET | /videos/{videoId}/comments | 取得指定影片的評論列表 |
POST | /videos/{videoId}/comments | 新增評論至指定影片 |
GET | /search?query={keyword} | 根據關鍵字搜尋影片 |
可能發生的效能問題
- 影片卡頓問題:尖峰時段(例如同時 100 萬使用者觀看)導致媒體伺服器資源耗盡,出現影片卡頓或播放中斷。
- CDN (Content Delivery Network) 部署範圍問題:全球用戶使用不同網路時,因 CDN 節點不足或頻寬不足,部分用戶播放體驗不佳。
- 快取層 (Cache Layer) 設計不足:熱門影片未能及時在快取內,造成影片載入時間過長。
分析步驟
- 了解系統背景,包含系統架構、溝通協定、可能會遇到的效能問題。
- 針對測試目的,了解真實的業務模型,並且設計出合理的測試情境。
- 決定效能測試的指標和監控,例如:多少用戶上傳影片,回應時間的合理範圍。
透過上面的資訊和步驟可以設計出測試情境,以下針對場景 1 所設計出來的測試情境。而這樣的測試叫做混合工作負載測試 (Mixed workload testing)
對一個系統進行多種不同類型的工作負載測試,這些工作可能包含讀取、寫入、資料處理或是網路請求等操作,透過模擬實際環境的不同需求,這種測試方法主要是測試執行不同類型的工作負在下系統的表現,並且確保系統能夠在這樣混合的情境下保持穩定和正常運作。
測試情境
測試情境 1:模擬用戶大量上傳影片,並且同時進行即時觀看,測試系統的寫入和讀取性能。
- 分析系統背景,Web 伺服器是本次我們要測試的主要目標,
- 針對測試目標,決定業務模型。通常可以透過過去歷史資料或合理計算評算要模擬 100 萬用戶同時在線,但其中 80 萬用戶進行即時關看影片,而 10 萬用戶則是上傳影片,10 萬用戶邊看影片邊上傳影片。
- 決定效能測試的指標和監控:
系統效能測試指標與監控
指標類別 | 描述 | 數值範圍 |
---|---|---|
吞吐量 (Throughput) | 系統每秒處理的請求數或資料量。 | |
影片觀看請求 (80 萬用戶) | 每秒觀看請求數應在此範圍內 | 80,000~100,000 reqs/s |
影片上傳請求 (10 萬用戶) | 每秒上傳請求數應在此範圍內 | 10,000~20,000 reqs/s |
同時觀看和上傳請求 | 每秒同時觀看和上傳請求數應在此範圍內 | 10,000~15,000 reqs/s |
回應時間 (Response Time) | 系統回應時間 | |
影片觀看請求 | 每秒觀看請求應在此範圍內完成 | 1~2 秒 |
影片上傳請求 | 每秒上傳請求應在此範圍內完成 | 2~5 秒 |
混合觀看和上傳請求 | 同時進行觀看和上傳操作,回應時間應保持此範圍 | 1~3 秒 |
同時使用者數量 (Concurrent Users) | 系統同時支援的最大用戶數 | |
影片觀看 | 進行觀看的用戶數 | 80 萬 |
影片上傳 | 進行上傳的用戶數 | 10 萬 |
混合操作 | 同時進行觀看和上傳的用戶數 | 10 萬 |
系統資源利用率 (Resource Utilization) | 系統資源使用情況,避免過載 | |
CPU 使用率 | 保持在此範圍內,避免過高導致過載 | 70%~85% |
記憶體使用率 | 保持在此範圍內,避免記憶體不足 | 60%~80% |
磁碟 I/O 使用率 | 保持在此範圍內,避免磁碟瓶頸 | 70%~85% |
網路頻寬使用率 | 保持在此範圍內,避免頻寬瓶頸 | 60%~80% |
錯誤率 (Error Rate) | 系統操作的失敗率 | |
影片觀看失敗 | 觀看失敗率應少於此數值 | < 0.5% |
影片上傳失敗 | 上傳失敗率應少於此數值 | < 1% |
影片流暢度 (Buffering Time) | 網路頻寬增長時,影片緩衝時間應保持此範圍 | 0.5~1 秒 |
資料庫性能 (Database Performance) | 讀寫查詢的處理時間 | |
讀取查詢 | 讀取應保持在此範圍內完成 | < 100ms |
寫入查詢 | 寫入應保持在此範圍內完成 | < 200ms |
延遲 (Latency) | 系統整體延遲,確保影片觀看和上傳的流暢性 | < 100ms |
K6 測試腳本
接著使用 K6 工具撰寫對應的測試腳本:這個腳本的目的是模擬 80 萬用戶觀看影片、10 萬用戶上傳影片,還有 10 萬用戶同時觀看和上傳影片,來測試系統在高負載下的效能。腳本分成不同的階段來模擬這些情境,並監控每秒的請求數和回應時間。
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 800000 }, // 80 萬觀看影片的用戶
{ duration: '30s', target: 100000 }, // 10 萬上傳影片的用戶
{ duration: '30s', target: 100000 }, // 10 萬同時觀看和上傳影片的用戶
{ duration: '1m', target: 0 }, // 快速降載至 0
],
thresholds: {
'http_req_duration': ['p(95)<2000'], // 95% 的請求必須在 2 秒內完成
'http_reqs': ['rate>100000'], // 每秒請求數需超過 10 萬
},
};
export default function () {
const userType = Math.floor(Math.random() * 3); // 隨機選擇行為:觀看、上傳或同時進行
if (userType === 0) {
// 模擬 觀看影片
const res = http.get('http://localhost:3333/videos');
check(res, {
'response time < 2s': (r) => r.timings.duration < 2000,
'status is 200': (r) => r.status === 200,
});
} else if (userType === 1) {
// 模擬 上傳影片
const res = http.post('http://localhost:3333/videos', { title: 'New Video', description: 'Test Video' });
check(res, {
'response time < 2s': (r) => r.timings.duration < 2000,
'status is 200': (r) => r.status === 200,
});
} else {
// 模擬 同時觀看和上傳影片
const viewRes = http.get('http://localhost:3333/videos');
check(viewRes, {
'response time < 2s': (r) => r.timings.duration < 2000,
'status is 200': (r) => r.status === 200,
});
const uploadRes = http.post('http://localhost:3333/videos', { title: 'New Video', description: 'Test Video' });
check(uploadRes, {
'response time < 2s': (r) => r.timings.duration < 2000,
'status is 200': (r) => r.status === 200,
});
}
sleep(1);
}
延伸練習
如果想要進一步挑戰,可以試著自己分析場景 2 和場景 3,並按照我們上面提到的步驟來設計和撰寫對應的測試場景與測試腳本。此外,試著找出上面測試腳本設定中的一些不合理之處,思考哪些地方可以進行調整或優化,這也是非常好的練習喔!
結論
在這次的效能測試中,我們模擬了多種情境,包括大規模用戶同時觀看、上傳影片,以及同時進行兩者的操作。透過這些測試場景,我們能夠及早發現系統在高負載下的潛在瓶頸,像是影片加載時間過長或上傳速度變慢等問題,讓我們在真正面對高流量時能更從容應對,並且不管今天是週幾,都值得放鬆一下,繼續前進!讓我們期待下次的挑戰,隨時保持最佳狀態!
Comments ()