iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
Vibe Coding

上岸用AI,看小白如何從無到有的用Vibe Codeing開發遊戲系列 第 29 篇

Day 29:上線前的沙盤推演 —— k6 高併發壓測模擬、Ktor Coroutines / HikariCP 調優與弱網環境模擬測試

  • 分享至 

  • xImage
  •  

歡迎來到第二十九天!經過了將近一個月的奮戰,我們的遊戲大廳、雙小遊戲、LINE LIFF 鑑權、金流 Webhook 與資安防護已全數建置完成。然而,在系統正式開放給使用者前,我們必須進行最關鍵的一步:上線前夕的沙盤推演 —— 高併發壓力測試模擬(Load Testing Simulation)與弱網環境容錯測試。

當活動上線出現瞬間流量爆發,數百位使用者同時開啟頁面、載入 WebGL 引擎、發起 OAuth2 登入並回傳遊戲分數時,如果伺服器因為連線池爆掉或 Coroutines 阻塞而跳出 502 Bad Gateway,將會嚴重影響系統穩定度。

為了防患於未然,我們在 Staging 預發布環境下進行完整的 k6 壓測模擬流程、Ktor 效能調優,以及 弱網環境模擬測試!


1. k6 高併發壓力測試預演與情境設計

我們採用 Go 語言打造的高效能壓測工具 k6,針對系統的核心瓶頸 —— 「LINE LIFF 登入鑑權 API」 與 「遊戲結算加分 API」 進行 Spike Test(突發高併發模擬演練)。

                        【k6 壓測模擬情境圖】
   ┌─────────────────────────────────────────────────────────┐
   │ 0s  ~ 30s  : 快速爬升至 100 VUs (Virtual Users)          │
   │ 30s ~ 1m30s: 衝刺至 500 VUs 尖峰併發 (模擬瞬間流量爆發)     │
   │ 1m30s~ 2m  : 維持 500 VUs 壓力高位進行 Stability 驗證     │
   │ 2m  ~ 2m30s: 平滑降壓至 0 VUs                            │
   └─────────────────────────────────────────────────────────┘

k6 JavaScript 壓力測試演練腳本 (stress_test_full.js):

import http from 'k6/http';
import { check, group, sleep } from 'k6';

// 壓測階段模擬設定
export const options = {
    stages: [
        { duration: '30s', target: 100 }, // 30 秒爬升至 100 併發
        { duration: '1m', target: 500 },  // 1 分鐘內衝刺至 500 併發
        { duration: '30s', target: 500 },  // 維持 500 併發持續壓力
        { duration: '30s', target: 0 },    // 降壓
    ],
    thresholds: {
        http_req_failed: ['rate<0.01'],   // 錯誤率必須低於 1%
        http_req_duration: ['p(95)<300'], // 95% 的請求必須在 300ms 內完成
    },
};

const BASE_URL = 'https://staging-api.yourstore.com/api/v1';

export default function () {
    group('LINE LIFF 快速登入預演模擬', function () {
        const loginPayload = JSON.stringify({
            idToken: "mock_line_id_token_for_k6_test_" + __VU
        });

        const params = {
            headers: {
                'Content-Type': 'application/json',
                'X-Signature': 'mock_k6_signature'
            },
        };

        const res = http.post(`${BASE_URL}/auth/login`, loginPayload, params);

        check(res, {
            '登入 HTTP 狀態碼為 200': (r) => r.status === 200,
            '登入回應時間 < 200ms': (r) => r.timings.duration < 200,
            '包含有效的 JWT Token': (r) => r.json('data.token') !== undefined,
        });
    });

    sleep(1); // 模擬使用者開頁後的閱讀與思考間隔
}

2. 預演瓶頸突破:Ktor Coroutines 與 HikariCP 連線池實戰調優

在初次模擬 500 VUs 壓測時,我們發現 API 回應時間飆升至 1.8 秒,甚至出現 HikariPool-1 - Connection is not available 的逾時錯誤。

透過瓶頸排查,我們對 Kotlin Ktor 執行緒池 (Dispatchers.IO) 與 HikariCP 連線池 進行了深度調優:

// Application.kt - Ktor 效能調優與連線池最佳化預演
fun Application.configureDatabases() {
    val config = HikariConfig().apply {
        jdbcUrl = "jdbc:postgresql://localhost:5432/game_db"
        driverClassName = "org.postgresql.Driver"
        username = System.getenv("DB_USER") ?: "postgres"
        password = System.getenv("DB_PASSWORD") ?: "secret"
        
        // 【HikariCP 連線池極限調優】
        maximumPoolSize = 30           // 配合容器規格,避免過度耗盡 DB 記憶體
        minimumIdle = 10               // 保持 10 個常駐空閒連線,防止瞬間爆發產生 Handshake 延遲
        idleTimeout = 30000            // 30 秒空閒回收
        connectionTimeout = 10000      // 連線等待逾時降至 10 秒,快速失敗 (Fail-Fast)
        maxLifetime = 1800000          // 30 分鐘強制重置連線,防止 PostgreSQL 死連線
        
        // 啟用 Leak Detection 偵測未關閉的 JDBC 事務
        leakDetectionThreshold = 5000  // 5 秒未歸還連線即發出 Alert
    }

    val dataSource = HikariDataSource(config)
    Database.connect(dataSource)
}

// 業務邏輯中,強制使用 Dispatchers.IO 避免阻塞 Ktor Netty 主執行緒
suspend fun <T> dbQuery(block: suspend () -> T): T =
    withContext(Dispatchers.IO) {
        transaction { block() }
    }

3. 弱網環境模擬與容錯機制測試

除了後端吞吐量,使用者的**行動網路弱網環境(Bad Network / 訊號死角)**也是必須考慮的體驗因素。我們在模擬環境中進行了各項弱網與容錯測試:

測試項目 模擬工具 / 設定 驗收標準 優化成果
弱網開頁速度 Chrome DevTools (3G Fast: 1.6Mbps / 150ms latency) 首頁 Loading 動畫在 2.5 秒內完成,不白屏 透過 Asset Bundle 分包,首包體積壓至 2.3MB
網路中斷重連 模擬中途斷網 5 秒再恢復連線 WebSocket 自動發起指數退避重連,資料不丟失 實作 WebSocketClient.ts 心跳與 Retry 機制
跨平台 WebView 相容性 iOS Safari / Android Chrome / LINE 內建 WebView 版面無跑版,按鈕熱區(Touch Target)\(\ge 48\text{dp}\) 修正 iOS 底部 Home Bar 擋住遊戲 UI 問題

4. 壓測演練數據對照

經過調優後,我們重新執行 500 VUs 的 k6 壓測演練,數據表現極為亮眼:

  • HTTP 請求總數:2 分鐘內完成 38,450 次 請求。
  • TPS (Transactions Per Second):穩定維持在 320+ TPS。
  • Average Response Time:從原本的 1800ms 降至 42ms。
  • HTTP 錯誤率 (Error Rate):0.00%(無任何 50x 或 40x 錯誤)。

💡 鐵人賽小知識:壓測中的「連線池枯竭(Connection Starvation)」與解決方案

Tech Tip:許多工程師誤以為 Concurrent 請求多就要把 maximumPoolSize 開到 200,結果反而因為 PostgreSQL 執行緒頻繁切換(Context Switching)導致 CPU 飆高崩潰。最佳實踐是 maximumPoolSize = (CPU 核心數 * 2) + 硬碟 IO 數,配合 Ktor Dispatchers.IO 非同步排隊,才能在高併發下保持 40ms 級別的極速回應!



上一篇
Day 28:營運數據埋點與監控系統建置 —— PostHog/GA4 埋點與 Sentry 異常即時告警
下一篇
Day 30:30 天鐵人賽完賽總結 —— 初次參賽心得、Vibe Coding 革命與未來 Firebase 雲端架構遷移展望
系列文
上岸用AI,看小白如何從無到有的用Vibe Codeing開發遊戲 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言