整體架構概覽
從 Genspark 移轉至 GCP,確保 500–10,000 人同時上線不會造成系統緩慢的完整設計藍圖
10,000+
同時上線人數上限
自動擴容支援
< 200ms
目標 API 回應時間
快取命中率 > 80%
99.9%
系統可用性 SLA
GCP 官方保障
$19–59
月費 USD (1000人)
含快取優化
GCP 完整架構圖
學生 / 老師 / 管理員
瀏覽器 Browser
瀏覽器 Browser
→
Cloud Armor
WAF + DDoS 防護
WAF + DDoS 防護
Cloud CDN
靜態資源加速
靜態資源加速
+
Load Balancer
自動分流
自動分流
→
Cloud Run
API 後端 (自動擴容)
API 後端 (自動擴容)
Cloud Storage
靜態檔案
靜態檔案
Firestore
主要資料庫
主要資料庫
Memorystore
Redis 快取
Redis 快取
Gemini API
AI 算力
AI 算力
Cloud Monitoring + Logging
監控 / 告警 / 日誌
監控 / 告警 / 日誌
Firebase Authentication
身份驗證
身份驗證
三層防慢架構設計原則
🛡️
第一層:邊緣加速
- Cloud CDN 快取靜態資源
- 全球邊緣節點(含台灣)
- HTML/CSS/JS 不觸碰伺服器
- 減少 90% 靜態請求負載
⚡
第二層:快取層
- Redis (Memorystore) 熱點資料
- 瀏覽器 sessionStorage 快取
- API 回應快取 5-60 分鐘
- 減少 60-80% 資料庫讀取
🔄
第三層:自動擴容
- Cloud Run 0→N 自動擴容
- Firestore 無限水平擴展
- 負載平衡自動分流
- 人數翻倍系統自動應對
核心保證:以下介紹的架構可讓系統從 500 人平滑擴展至 10,000+ 人,且無需手動調整伺服器。只要流量增加,GCP 自動增加運算資源;流量減少,自動縮回節省費用。
效能瓶頸分析
現有系統在高並發下的潛在問題點,以及對應的解決方向
瓶頸診斷雷達
高風險瓶頸
1
🔴 Tables API 並發限制(最高風險)
1000 人 × 3 次/分 = 3,000 請求/分鐘,Genspark 無公開限流文件,易觸發 429 錯誤。GCP Firestore 沒有此限制,自動擴展。
2
🔴 student.html 體積過大(581 KB)
首次載入需下載 581 KB HTML + 200+ KB JS,在行動網路(4G)需 3-8 秒。需拆分模組 + 啟用 Gzip 壓縮。
3
🔴 Gemini API Key 暴露在前端
任何人可從開發者工具取得 API Key,遭濫用將導致費用暴增或服務中斷。必須移至後端保管。
中等風險項目
4
🟡 無 sessionStorage 快取
每次頁面切換都重新呼叫 API,相同資料反覆請求。加入快取可減少 60% API 呼叫。
5
🟡 db-words.js 等大型 JS 一次全載
db-words.js (71 KB)、db-exam.js (70 KB) 等在用到前就全部載入,佔用記憶體。應改為動態 import()。
6
🟡 無監控與告警機制
目前無法得知系統是否正常運行、哪個模組出錯。在 GCP 需設定 Cloud Monitoring 告警。
請求量預估(1000人同時上線)
瓶頸對照表
| 瓶頸點 | 現狀(Genspark) | 移轉後(GCP) | 改善幅度 |
|---|---|---|---|
| API 並發 | ❌ 未知上限 | ✅ Firestore 自動擴展 | ↑ 無限 |
| 靜態資源 | ⚠️ 單一節點 | ✅ 全球 CDN 邊緣 | ↑ 90% |
| API 回應時間 | ⚠️ 200-800ms | ✅ <100ms(快取) | ↑ 80% |
| 資料庫讀寫 | ❌ 無快取 | ✅ Redis 快取層 | ↑ 70% |
| AI API 安全 | ❌ Key 暴露前端 | ✅ 後端環境變數 | ✅ 安全 |
| 自動擴容 | ❌ 無法擴展 | ✅ Cloud Run 自動 | 0→N 彈性 |
CDN 靜態加速策略
讓靜態資源從離使用者最近的邊緣節點提供,大幅降低載入時間
靜態資源(HTML/CSS/JS/圖片/音效)佔頁面載入的 80-90%。透過 CDN,這些資源從台灣本地節點提供,延遲從 300ms 降至 <20ms。
部署靜態資源到 GCP Cloud Storage + CDN
1
建立 GCS Bucket 並啟用靜態網站托管
# 建立儲存桶(asia-east1 = 台灣最近)
gsutil mb -l asia-east1 gs://wayne-english-app
# 設定公開讀取
gsutil iam ch allUsers:objectViewer gs://wayne-english-app
# 設定靜態網站入口
gsutil web set -m index.html -e 404.html gs://wayne-english-app
# 上傳所有靜態檔案(-Z 自動 gzip 壓縮)
gsutil -m -h "Cache-Control:public,max-age=3600" \
cp -Z -r ./*.html gs://wayne-english-app/
gsutil -m -h "Cache-Control:public,max-age=86400" \
cp -Z -r ./js/ gs://wayne-english-app/js/
gsutil -m -h "Cache-Control:public,max-age=604800" \
cp -Z -r ./images/ gs://wayne-english-app/images/
2
設定 Cloud CDN(Google Cloud Console)
# 建立負載平衡器 + 啟用 CDN(也可用 Console UI)
gcloud compute backend-buckets create wayne-backend \
--gcs-bucket-name=wayne-english-app \
--enable-cdn \
--cache-mode=CACHE_ALL_STATIC
# 建立 URL 映射
gcloud compute url-maps create wayne-url-map \
--default-backend-bucket=wayne-backend
# 建立 HTTPS Proxy(需先申請 SSL 憑證)
gcloud compute managed-ssl-certificates create wayne-cert \
--domains=english.yourschool.tw
gcloud compute target-https-proxies create wayne-https-proxy \
--url-map=wayne-url-map \
--ssl-certificates=wayne-cert
3
快取標頭設定建議
| 資源類型 | 快取時間 | 說明 |
|---|---|---|
| HTML 頁面 | Cache-Control: public, max-age=3600 | 1小時,功能更新後手動清除 |
| JS 資料庫(db-*.js) | Cache-Control: public, max-age=86400 | 1天,詞庫更新後清除 |
| 圖片 / 音效 | Cache-Control: public, max-age=604800 | 7天,幾乎不更新 |
| API 回應 | Cache-Control: private, max-age=0 | 不快取,每次即時 |
啟用 Gzip 壓縮(降低傳輸量)
目前各主要檔案大小 vs 壓縮後預估:
| 檔案 | 原始大小 | Gzip 後 | 節省 |
|---|---|---|---|
| student.html | 581 KB | ~145 KB | 75% |
| db-words.js | 71 KB | ~22 KB | 69% |
| db-exam.js | 70 KB | ~21 KB | 70% |
| db-grammar.js | 50 KB | ~16 KB | 68% |
| teacher.html | 63 KB | ~20 KB | 68% |
GCS 上傳時自動啟用壓縮:
# -Z 參數 = 自動 gzip 並設定 Content-Encoding
gsutil cp -Z index.html gs://wayne-english-app/
# 驗證:檢查 Content-Encoding header
curl -I https://your-domain.com/student.html
# 應看到:Content-Encoding: gzip
資料庫擴展策略
Firestore 的水平擴展設計,以及如何優化讀寫操作避免過多費用
Tables API → Firestore 對照轉換
❌ 舊版:Genspark Tables API
// 舊版:直接呼叫 Tables API
async function loadProgress() {
const res = await fetch(
'tables/student_progress?limit=100'
);
const data = await res.json();
return data.data;
}
async function saveProgress(id, score) {
await fetch(`tables/student_progress/${id}`, {
method: 'PATCH',
body: JSON.stringify({ total_score: score })
});
}
✅ 新版:Firestore SDK
// 新版:Firestore(加入 sessionStorage 快取)
import { getFirestore, doc, getDoc, setDoc }
from 'firebase/firestore';
async function loadProgress(userId) {
// 先查 sessionStorage
const cached = sessionStorage.getItem('progress');
if (cached) return JSON.parse(cached);
const db = getFirestore();
const snap = await getDoc(
doc(db, 'student_progress', userId)
);
const data = snap.data();
sessionStorage.setItem('progress', JSON.stringify(data));
return data;
}
Firestore 資料結構設計(對應現有 Tables)
// Firestore Collection 結構(對應現有 4 個 Tables)
{
// 對應 users table
"users": {
"[userId]": {
username, display_name, role, status,
email, avatar_key, last_login, login_count
}
},
// 對應 student_progress table
"student_progress": {
"[userId]": {
streak, last_study_date, total_words_learned,
total_score, level, badges: [...]
}
},
// 對應 activity_log table(Sub-collection 設計)
"users": {
"[userId]/activity_logs/[logId]": {
activity_type, module_name, score,
duration_seconds, timestamp
}
},
// 對應 teacher_messages table
"teacher_messages": {
"[msgId]": {
teacher_id, student_id, message_type,
content, is_read, created_at
}
},
// 對應 weekly_tasks + class_groups tables
"weekly_tasks": { ... },
"class_groups": { ... }
}
Firestore 讀寫優化(降低成本)
✅ 批次寫入
將多次小更新合併為一次 batch write,從每分鐘 5-10 次寫入降至 1 次。
const batch = writeBatch(db);
batch.update(ref1, { score: 95 });
batch.update(ref2, { streak: 7 });
await batch.commit(); // 1次寫入
✅ 定時批次寫入
學習過程中快取到記憶體,每 60 秒或離頁時才寫入 Firestore。
// 離頁時強制寫入
window.addEventListener('beforeunload',
() => flushProgress()
);
// 定時每60秒同步
setInterval(syncProgress, 60000);
✅ 只讀必要欄位
使用 field mask 只取需要的欄位,減少傳輸量與讀取費用。
// 只取 score 和 streak
const snap = await getDoc(
doc(db, 'student_progress', uid)
);
// 僅存取必要欄位
const { score, streak } = snap.data();
後端自動擴容
Cloud Run 是最適合本系統的後端選擇:只在有請求時才運行,自動從 0 擴展到 1000+ 個實例
Cloud Run 自動擴容原理
Cloud Run 後端完整程式碼(Node.js)
// server.js - Cloud Run 後端(安全保管 Gemini API Key)
const express = require('express');
const { GoogleGenerativeAI } = require('@google/generative-ai');
const admin = require('firebase-admin');
const rateLimit = require('express-rate-limit');
const app = express();
app.use(express.json());
// ✅ CORS 設定(只允許自己的網域)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin',
process.env.ALLOWED_ORIGIN || 'https://english.yourschool.tw');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
// ✅ 速率限制:每個 IP 每分鐘最多 20 次 AI 請求
const aiLimiter = rateLimit({
windowMs: 60 * 1000,
max: 20,
message: { error: '請求過於頻繁,請稍候再試' }
});
// ✅ Gemini AI 端點(API Key 存在環境變數,前端看不到)
app.post('/api/ai/chat', aiLimiter, async (req, res) => {
const { prompt, context } = req.body;
const genAI = new GoogleGenerativeAI(process.env.GEMINI_KEY);
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
const result = await model.generateContent(prompt);
res.json({ text: result.response.text() });
});
// ✅ 前端改成呼叫後端
// 舊版:fetch('https://generativelanguage.googleapis.com/v1beta/...')
// 新版:fetch('/api/ai/chat', { method:'POST', body:JSON.stringify({prompt}) })
app.listen(process.env.PORT || 8080);
部署 Cloud Run
# 1. 建立 Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
# 2. 建置 & 推送到 Artifact Registry
gcloud builds submit --tag asia-east1-docker.pkg.dev/[PROJECT]/wayne/api
# 3. 部署到 Cloud Run(設定自動擴容)
gcloud run deploy wayne-api \
--image asia-east1-docker.pkg.dev/[PROJECT]/wayne/api \
--region asia-east1 \
--min-instances 0 \ # 無流量時縮至 0(省費)
--max-instances 100 \ # 最大 100 個實例
--concurrency 80 \ # 每實例最多 80 並發
--memory 512Mi \
--set-env-vars GEMINI_KEY=your-key-here \
--allow-unauthenticated
自動擴容效果:500 人同時上線 → Cloud Run 自動啟動 ~7 個實例(每實例 80 並發),5000 人 → 自動擴至 63 個實例,無需人工介入。縮容時自動關閉多餘實例節省費用。
多層快取策略
從瀏覽器到資料庫建立三層快取,讓 80% 的請求不需觸碰資料庫
快取層次架構
Layer 1
瀏覽器
瀏覽器
sessionStorage / localStorage
學習進度、使用者資料、訊息列表。快取時效:sessionStorage = 關頁清空,localStorage = 永久。命中率:90%(同一 session 內多次查詢)
Layer 2
CDN 快取
CDN 快取
Cloud CDN Edge Cache
靜態 HTML/JS/CSS/圖片。快取時效:依 Cache-Control 設定(1小時~7天)。命中率:95%(熱門頁面)。全球 PoP 節點,台灣用戶從台北/彰化節點取得。
Layer 3
Redis
Redis
Cloud Memorystore (Redis)
熱點 API 回應、班級排行榜、老師訊息列表。快取時效:5-60 分鐘。回應速度:<5ms(vs Firestore 50-200ms)。月費:~$30 USD(基本方案)。
前端 sessionStorage 快取實作(直接可用)
// ✅ 可立即加入 student.html / teacher.html 的快取工具
const Cache = {
set(key, data, ttlMinutes = 60) {
sessionStorage.setItem(key, JSON.stringify({
data,
exp: Date.now() + ttlMinutes * 60000
}));
},
get(key) {
const item = sessionStorage.getItem(key);
if (!item) return null;
const { data, exp } = JSON.parse(item);
if (Date.now() > exp) {
sessionStorage.removeItem(key);
return null;
}
return data;
},
del(key) { sessionStorage.removeItem(key); }
};
// 使用範例:快取學習進度 60 分鐘
async function getProgress(userId) {
const cached = Cache.get(`progress_${userId}`);
if (cached) {
console.log('⚡ 從快取取得進度');
return cached;
}
const data = await fetchFromFirestore(userId);
Cache.set(`progress_${userId}`, data, 60);
return data;
}
// 使用範例:快取班級訊息 5 分鐘
async function getMessages(classId) {
const cached = Cache.get(`msgs_${classId}`);
if (cached) return cached;
const msgs = await loadMessagesFromDB(classId);
Cache.set(`msgs_${classId}`, msgs, 5);
return msgs;
}
快取後 API 呼叫量估算
AI API 管理與限流
確保 Gemini API 不被濫用,同時控制成本並維持服務品質
重要安全警告:目前 Gemini API Key 直接寫在 student.html 前端,任何人打開開發者工具(F12)即可看到並濫用。移轉 GCP 後必須將 Key 移至後端。
AI API 三層保護機制
❌ 現狀(危險)
- API Key 在 student.html 明碼
- 任何人可無限呼叫
- 遭盜用費用暴增
- 無使用量追蹤
保護層 1:後端代理
- Key 存在 Cloud Run 環境變數
- 前端只看到 /api/ai/chat
- 驗證 Firebase Token 才放行
- 記錄每次呼叫 Log
保護層 2+3:限流
- 每用戶每分鐘最多 10 次
- 每 IP 每分鐘最多 20 次
- 每日費用上限警報
- Cloud Armor 封鎖可疑流量
前端改寫:安全呼叫 AI
❌ 舊版(不安全)
// ❌ Key 直接在前端
const GEMINI_KEY = 'AIzaSy...';
const res = await fetch(
`https://generativelanguage.googleapis.com/v1beta/
models/gemini-2.0-flash:generateContent?key=${GEMINI_KEY}`,
{
method: 'POST',
body: JSON.stringify({ contents: [...] })
}
);
✅ 新版(安全)
// ✅ 呼叫自己的後端(Key 在伺服器)
async function callGemini(prompt) {
const token = await
firebase.auth().currentUser.getIdToken();
const res = await fetch('/api/ai/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ prompt })
});
return (await res.json()).text;
}
AI 費用預警設定
# 在 GCP Console 設定費用警報(CLI 方式)
gcloud billing budgets create \
--billing-account=BILLING_ACCOUNT_ID \
--display-name="Wayne English AI Budget" \
--budget-amount=50USD \ # 月費上限 $50 USD
--threshold-rule=percent=80 \ # 80% 時發 Email 警告
--threshold-rule=percent=100 # 100% 時再次警告
# 若需要自動停止服務(超支時)
# 可透過 Pub/Sub + Cloud Function 自動停用 API Key
監控與告警設定
上線後如何即時掌握系統健康狀況,提早發現問題
關鍵監控指標(KPI)
⏱️
API 回應時間
目標 <200ms
警報 >500ms
警報 >500ms
❌
錯誤率
目標 <1%
警報 >5%
警報 >5%
👥
同時上線人數
即時監控
預測容量需求
預測容量需求
💰
每日費用
警報 >$3/天
自動通知
自動通知
Cloud Monitoring 告警設定
# 方法 1:直接在 GCP Console > Monitoring > Alerting 設定(推薦 UI 操作)
# 方法 2:用 gcloud CLI
## 告警 1:API 回應時間 > 500ms
gcloud alpha monitoring policies create \
--notification-channels=EMAIL_CHANNEL_ID \
--condition-filter='metric.type="run.googleapis.com/request_latencies"
resource.type="cloud_run_revision"
metric.labels.response_code_class="2xx"' \
--condition-threshold-value=500 \
--condition-threshold-comparator=GT \
--display-name="API 回應超過 500ms"
## 告警 2:錯誤率 > 5%
gcloud alpha monitoring policies create \
--condition-filter='metric.type="run.googleapis.com/request_count"
metric.labels.response_code_class="5xx"' \
--display-name="伺服器錯誤率過高"
## 告警 3:Cloud Run 實例數超過 50(預期用量異常)
gcloud alpha monitoring policies create \
--condition-filter='metric.type="run.googleapis.com/container/instance_count"' \
--condition-threshold-value=50 \
--display-name="異常高並發警告"
告警通知與對應處理 SOP
| 告警類型 | 觸發條件 | 通知方式 | 對應處理 | 優先級 |
|---|---|---|---|---|
| API 回應過慢 | >500ms 持續 5 分鐘 | Email + LINE Bot | 檢查 Cloud Run 實例數 / Firestore 查詢 | ⚠️ 高 |
| 伺服器錯誤 | 5xx 錯誤率 >5% | Email + LINE Bot | 查看 Cloud Logging,找 Error 原因 | 🔴 緊急 |
| 費用異常 | 日費用 >$3 USD | 檢查是否有異常流量或 API 濫用 | ⚠️ 高 | |
| Cloud Run 大量擴容 | 實例 >50 | 確認是正常尖峰或 DDoS 攻擊 | ℹ️ 中 | |
| Firestore 讀取過多 | 日讀 >100,000 次 | 確認快取是否正常運作 | ℹ️ 中 |
建議監控工具組合
- Cloud Monitoring – 即時指標儀表板,設定告警規則,免費包含基本功能
- Cloud Logging – 所有 API 請求 / 錯誤日誌集中管理,可查詢分析
- Cloud Trace – 追蹤每個請求的完整路徑,找出慢查詢
- Firebase Performance Monitoring – 前端頁面載入速度、網路請求追蹤(免費)
- GCP Billing 費用警報 – 設定月費上限,超過自動 Email 通知
成本規劃
不同人數規模下的月費估算,以及如何控制成本
人數 vs 月費趨勢
🤖 AI 算力費用詳細拆解(Gemini API)
重要提醒:AI 算力費用是本系統成本中最大的單一項目,且會隨使用頻率大幅波動。以下為三種情境的詳細計算。
😊 輕度使用(保守估計)
每人每天:2 次 AI 互動
每次 Token:輸入 300 + 輸出 200 = 500 token
每日總量:1,000人 × 2次 × 500 = 100萬 token
月總量:100萬 × 30天 = 3,000萬 token
輸入費:1,500萬 × $0.075/1M = $1.1
輸出費:1,500萬 × $0.30/1M = $4.5
月費合計:≈ $6 USD(NT$192)
每次 Token:輸入 300 + 輸出 200 = 500 token
每日總量:1,000人 × 2次 × 500 = 100萬 token
月總量:100萬 × 30天 = 3,000萬 token
輸入費:1,500萬 × $0.075/1M = $1.1
輸出費:1,500萬 × $0.30/1M = $4.5
月費合計:≈ $6 USD(NT$192)
😐 中度使用(一般情境)
每人每天:5 次 AI 互動
每次 Token:輸入 400 + 輸出 300 = 700 token
每日總量:1,000人 × 5次 × 700 = 350萬 token
月總量:350萬 × 30天 = 1.05億 token
輸入費:5,250萬 × $0.075/1M = $3.9
輸出費:5,250萬 × $0.30/1M = $15.8
月費合計:≈ $20 USD(NT$640)
每次 Token:輸入 400 + 輸出 300 = 700 token
每日總量:1,000人 × 5次 × 700 = 350萬 token
月總量:350萬 × 30天 = 1.05億 token
輸入費:5,250萬 × $0.075/1M = $3.9
輸出費:5,250萬 × $0.30/1M = $15.8
月費合計:≈ $20 USD(NT$640)
😰 重度使用(作文批改為主)
每人每天:10 次 AI 互動
每次 Token:輸入 800 + 輸出 600 = 1,400 token
每日總量:1,000人 × 10次 × 1,400 = 1,400萬 token
月總量:1,400萬 × 30天 = 4.2億 token
輸入費:2.1億 × $0.075/1M = $15.8
輸出費:2.1億 × $0.30/1M = $63.0
月費合計:≈ $79 USD(NT$2,528)
每次 Token:輸入 800 + 輸出 600 = 1,400 token
每日總量:1,000人 × 10次 × 1,400 = 1,400萬 token
月總量:1,400萬 × 30天 = 4.2億 token
輸入費:2.1億 × $0.075/1M = $15.8
輸出費:2.1億 × $0.30/1M = $63.0
月費合計:≈ $79 USD(NT$2,528)
本系統 AI 功能使用頻率分析
| AI 功能 | 使用場景 | 每次 Token 估算 | 使用頻率 | 費用占比 |
|---|---|---|---|---|
| ✏️ 作文/翻譯批改 | student.html | 輸入600 + 輸出400 = 1,000 | ⭐⭐⭐ 最高 | ~45% |
| 🗣️ 口語對話(chat.html) | Chat 場景 | 輸入500 + 輸出350 = 850 | ⭐⭐⭐ 高 | ~35% |
| 📝 出題生成 | student.html 週測 | 輸入300 + 輸出600 = 900 | ⭐⭐ 中 | ~12% |
| 🔊 Gemini STT 語音辨識 | chat.html 麥克風 | 音訊 token(較貴) | ⭐⭐ 中 | ~8% |
| 🌱 國小 AI 批改 | elementary.html | 輸入200 + 輸出200 = 400 | ⭐ 低 | ~5% |
💡 Gemini 2.0 Flash 定價(2025年):
輸入 Token:$0.075 / 1M token(免費額度:每日 100萬 token,每分鐘 15 次)
輸出 Token:$0.30 / 1M token
語音/音訊 Token:$0.075 / 1M token(約普通文字的 1-2 倍成本)
⚠️ 注意:超過免費額度才開始計費,1000人一般情況下每天會超過免費額度約 2-3 倍
輸入 Token:$0.075 / 1M token(免費額度:每日 100萬 token,每分鐘 15 次)
輸出 Token:$0.30 / 1M token
語音/音訊 Token:$0.075 / 1M token(約普通文字的 1-2 倍成本)
⚠️ 注意:超過免費額度才開始計費,1000人一般情況下每天會超過免費額度約 2-3 倍
完整月費總表(含 AI 算力,1000人)
以下費用已包含 Gemini AI 算力費用(以中度使用情境計算)。AI 費用因使用習慣差異大,請參考上方三種情境選擇適合的估算。
| 服務項目 | 費用類型 | 免費額度 | 500 人 | 1,000 人 | 3,000 人 | 10,000 人 |
|---|---|---|---|---|---|---|
| Cloud Storage(靜態) | 基礎架構 | 5 GB / 月 | $0 | $0 | $1 | $3 |
| Cloud CDN | 基礎架構 | 首 10 GB 免費 | $1 | $3 | $8 | $25 |
| Firebase Auth | 基礎架構 | 10K MAU/月 | $0 | $0 | $30 | $600 |
| Firestore(含快取優化) | 資料庫 | 50K讀/1K寫/天 | $0 | $12 | $35 | $110 |
| Cloud Run(後端 API) | 後端 | 200萬請求/月 | $0 | $2 | $6 | $20 |
| 🤖 Gemini API 輕度使用 | AI 算力 | 每日 100萬 token | $3 | $6 | $18 | $60 |
| 🤖 Gemini API 中度使用 | AI 算力 | $10 | $20 | $60 | $200 | |
| 🤖 Gemini API 重度使用 | AI 算力 | $40 | $79 | $237 | $790 | |
| Memorystore Redis(選配) | 快取 | 無免費 | – | $30 | $30 | $60 |
| 合計(中度 AI,不含Redis) | – | – | $14 | $37 | $110 | $358 |
| 合計(中度 AI,含 Redis) | – | – | $14 | $67 | $140 | $418 |
| 約台幣(×32) | – | – | NT$448 | NT$2,144 | NT$4,480 | NT$13,376 |
NT$2,144
1,000人 / 月(含AI算力)
中度使用,含Redis快取
NT$99/人/月
收費方案(建議)
1,000人月收 NT$99,000
2.2%
伺服器費 / 月收入
$2,144 ÷ $99,000 = 2.2%
⚠️ AI 算力是最大變數:若學生大量使用作文批改 + 口語對話,費用會從 $20 跳升至 $79 USD(重度情境)。建議:
- 設定每用戶每日 AI 呼叫上限(建議 10-20 次)
- 設定 GCP 費用警報(超過 $50 USD/月 自動通知)
- 監控每日 Token 使用量,超出預算時通知管理員
降低成本的 5 個策略
1
啟用 sessionStorage 快取(立即可做,免費)
減少 60-70% Firestore 讀取次數。1000 人的 Firestore 費用從 $35 降至 $12。今天就可以加入 student.html 和 teacher.html。
2
定時批次寫入(每 60 秒一次)
學習過程中暫存到記憶體,每 60 秒或離頁時才寫入。Firestore 寫入費用($0.9/100K)遠高於讀取,此策略效益最高。
3
AI Token 限制(每用戶每日上限)
設定每用戶每天最多呼叫 AI 20 次,防止少數用戶大量消耗 Token 費用。記錄在 Firestore,後端驗證。
4
靜態資源拆分 + 按需載入
將 student.html (581 KB) 拆分為核心頁面 + 按需載入模組。只在進入文法頁面時才載入 db-grammar.js,減少首屏資料量 70%。
5
Cloud Run 設定最小實例為 0(非上課時間省費)
學生通常在上課時間(8am-6pm)使用,其餘時間設定 min-instances=0,Cloud Run 縮至 0 個實例,節省 40-50% 費用。
移轉步驟指南
從 Genspark 移轉至 GCP Firebase 的完整步驟,預計 1-2 天完成
移轉時程規劃
D1
Day 1 上午(2小時):建立 GCP 環境
申請 GCP 帳號 → 建立專案 → 啟用 Firebase → 建立 Firestore → 設定 Firebase Auth → 建立 Cloud Storage Bucket → 申請 SSL 憑證
D1
Day 1 下午(4小時):移轉資料
從 Genspark Tables API 導出所有資料(users, student_progress, activity_log, teacher_messages, weekly_tasks, class_groups)→ 匯入 Firestore
D2
Day 2 上午(3小時):改寫前端程式碼
改寫 auth.js(Tables API Login → Firebase Auth)→ 改寫 API 呼叫(Tables API → Firestore SDK)→ 改寫 Gemini 呼叫(前端直呼 → 後端代理)
D2
Day 2 下午(2小時):部署 & 測試
部署靜態檔案到 Cloud Storage → 部署 Cloud Run 後端 → 設定 CDN → DNS 切換 → 全功能測試(學生/老師/管理員)
D3
Day 3(選擇性):監控設定 & 優化
設定 Cloud Monitoring 告警 → 設定費用警報 → 加入 sessionStorage 快取 → 壓力測試(模擬 500 人並發)→ 正式上線
資料移轉腳本
// migrate.js - 從 Genspark Tables API 匯出到 Firestore
const admin = require('firebase-admin');
const fetch = require('node-fetch');
admin.initializeApp({ credential: admin.credential.applicationDefault() });
const db = admin.firestore();
const TABLES = [
'users',
'student_progress',
'activity_log',
'teacher_messages',
'weekly_tasks',
'class_groups'
];
async function migrateTable(tableName) {
console.log(`⏳ 移轉 ${tableName}...`);
let page = 1, total = 0;
while (true) {
const res = await fetch(
`https://your-genspark-project/tables/${tableName}?page=${page}&limit=100`
);
const { data, total: count } = await res.json();
if (!data.length) break;
const batch = db.batch();
data.forEach(row => {
const ref = db.collection(tableName).doc(row.id);
batch.set(ref, { ...row, migrated_at: Date.now() });
});
await batch.commit();
total += data.length;
console.log(` ✅ 已移轉 ${total}/${count} 筆`);
page++;
if (data.length < 100) break;
}
console.log(`✅ ${tableName} 移轉完成,共 ${total} 筆`);
}
(async () => {
for (const table of TABLES) {
await migrateTable(table);
}
console.log('🎉 所有資料移轉完成!');
})();
auth.js 改寫對照
❌ 舊版 auth.js(Tables API)
// 舊版:SHA-256 hash + Tables API 查詢
async function login(username, password) {
const hash = await sha256(password);
const res = await fetch(
`tables/users?search=${username}`
);
const { data } = await res.json();
const user = data.find(u =>
u.username === username &&
u.password_hash === hash
);
if (!user) throw new Error('帳號或密碼錯誤');
return user;
}
✅ 新版 auth.js(Firebase Auth)
// 新版:Firebase Auth(安全 + 支援 OAuth)
import { getAuth, signInWithEmailAndPassword }
from 'firebase/auth';
async function login(email, password) {
const auth = getAuth();
const cred = await signInWithEmailAndPassword(
auth, email, password
);
// 取得用戶角色(存在 Firestore)
const db = getFirestore();
const snap = await getDoc(
doc(db, 'users', cred.user.uid)
);
return { ...cred.user, ...snap.data() };
}
上線檢查清單
正式上線前必須完成的所有項目,確保系統穩定可靠
安全性檢查
- API Key 已移至後端環境變數(不在前端)
- Firebase Auth 已設定,舊版帳密登入已停用
- Firestore Security Rules 已設定(每人只能讀自己的資料)
- Cloud Armor WAF 已啟用(DDoS 防護)
- HTTPS 憑證已設定(SSL)
- CORS 設定只允許自己的網域
- AI API 速率限制已設定(每用戶每分 10 次)
效能檢查
- Cloud CDN 已啟用,靜態資源從邊緣節點提供
- Gzip 壓縮已啟用(gsutil cp -Z)
- sessionStorage 快取已加入前端程式碼
- Firestore 批次寫入已實作(每 60 秒)
- Cloud Run 自動擴容已設定(min=0, max=100)
- 頁面首屏載入 <3 秒(可用 Lighthouse 測試)
- Firestore Indexes 已建立(避免全表掃描)
監控與告警
- Cloud Monitoring 已啟用,儀表板已設定
- API 回應時間告警 (>500ms) 已設定
- 錯誤率告警 (>5%) 已設定
- 費用警報 ($50 USD/月) 已設定
- 告警通知 Email 已確認可收到
功能測試
- 學生登入 / 登出功能正常
- 老師登入 / 儀表板功能正常
- 管理員 admin 功能正常
- AI 作文批改功能正常
- 老師訊息發送 / 學生接收正常
- 學習進度儲存 / 讀取正常
- 壓力測試:模擬 500 人並發無異常
壓力測試工具推薦
# 方法 1:k6(免費,推薦)
brew install k6
# 建立測試腳本 stress-test.js
import http from 'k6/http';
export const options = {
vus: 500, // 500 虛擬用戶
duration: '60s', // 持續 60 秒
};
export default function () {
http.get('https://english.yourschool.tw/');
http.post('https://api.yourschool.tw/api/progress', ...);
}
# 執行測試
k6 run stress-test.js
# 方法 2:Apache Bench(快速測試)
ab -n 10000 -c 500 https://english.yourschool.tw/
# -n = 總請求數 -c = 並發數
🎉 恭喜完成所有檢查!您的系統已準備好在 GCP 上承受 500–10,000 人同時上線,且不會因人數增加而緩慢。接下來只需要關注監控儀表板,享受自動擴容的便利。