iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Build on Google AI

用 Google AI 生態系 30 天從零打造一個全棧 AI SaaS 服務系列 第 28 篇

Day 28 -【系統整體架構與防禦】全棧架構總覽、API Rate Limiting 限流防禦與生產級災難復原演練

  • 分享至 

  • xImage
  •  

在昨天的 [Day 27] 中,我們成功導入了 Gemini 1.5 Function Calling,賦予 OmniVibe AI 自動化調用外部工具(如發送 Email 與建立 Google 日曆行程)的自主代理(Agentic)能力。

隨著鐵人賽進入尾聲,我們的 OmniVibe AI 已經從一個簡單的概念原型,進化為具備多模態、快取、檢索、外掛、非同步佇列與金流的多通道生產級 AI SaaS。

在將系統正式全面推向對外公眾營運之前,我們必須進行最後也是最關鍵的一道安全防線 —— 全棧架構總覽複查、API Rate Limiting 限流防禦,以及生產級災難復原(Disaster Recovery)演練!


🏛️ OmniVibe AI 全棧生產級架構總覽

在進入防禦工程前,讓我們透過架構圖來俯瞰 OmniVibe AI 這 28 天來打造的完整生態系:

graph TD
    subgraph 1. 多通道客戶端 (Clients)
        Web[Next.js 15 Web Dashboard]
        Ext[Chrome Extension Side Panel]
        Line[LINE Bot Messaging API]
    end

    subgraph 2. 邊緣與伺服器層 (Cloud Run Edge)
        CloudRun[Google Cloud Run (Next.js Standalone)]
        Middleware[Upstash Rate Limiter & Auth Guard]
        CloudRun --> Middleware
    end

    subgraph 3. 非同步與工作佇列 (Async Workers)
        CloudTasks[Google Cloud Tasks / PubSub] --> Worker[Background Worker Endpoints]
    end

    subgraph 4. AI 智慧引擎與雙層快取 (Google AI & Caching)
        Worker --> L1[Qdrant Semantic Cache]
        Worker --> L2[Gemini Context Cache]
        L2 --> Gemini[Gemini 1.5 Flash / Pro API]
        Gemini --> RAG[Hybrid RAG & Function Calling]
    end

    subgraph 5. 資料持久化與商業金流 (Storage & Monetization)
        CloudRun --> Supabase[(Supabase PostgreSQL)]
        CloudRun --> Stripe[Stripe Checkout & Webhooks]
    end

    Web --> CloudRun
    Ext --> CloudRun
    Line --> CloudRun


🛡️ 第一步:實作 API Rate Limiting 限流防禦 (src/lib/middleware/rate-limit.ts)

在生產環境中,若遭到惡意腳本刷 API 請求,不僅會讓我們的 Supabase 資料庫崩潰,更會瞬間消耗掉龐大的 Google AI 費用。我們使用 Upstash Redis 實作基於 Fixed Window 的 IP / User ID 雙重限流機制:

// src/lib/middleware/rate-limit.ts
import { Redis } from '@upstash/redis';
import { NextRequest, NextResponse } from 'next/server';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL || '',
  token: process.env.UPSTASH_REDIS_REST_TOKEN || '',
});

export async function checkRateLimit(req: NextRequest, userId: string = 'anonymous') {
  const ip = req.headers.get('x-forwarded-for') || '127.0.0.1';
  const identifier = `rate_limit:${userId !== 'anonymous' ? userId : ip}`;
  
  const WINDOW_SECONDS = 60; // 60 秒時間窗口
  const MAX_REQUESTS = 20;   // 每分鐘最多 20 次 AI 請求

  try {
    const currentRequests = await redis.incr(identifier);

    if (currentRequests === 1) {
      await redis.expire(identifier, WINDOW_SECONDS);
    }

    if (currentRequests > MAX_REQUESTS) {
      console.warn(`[Rate Limit Exceeded] 請求過於頻繁,識別碼: ${identifier}`);
      return {
        success: false,
        response: NextResponse.json(
          { error: '請求過於頻繁(Rate Limit Exceeded),請稍後再試。' },
          { status: 429, headers: { 'Retry-After': '60' } }
        ),
      };
    }

    return { success: true };
  } catch (err) {
    console.error('[Rate Limit Error]:', err);
    // 容錯機制:Redis 異常時放行,避免單點故障阻斷業務
    return { success: true };
  }
}

在 API Route 中無縫導入限流檢查:

// src/app/api/ai/fast-distill/route.ts (片段)
import { checkRateLimit } from '@/lib/middleware/rate-limit';

export async function POST(req: NextRequest) {
  const { userId } = await req.json();

  // 1. 執行速率限制防禦
  const rateLimitCheck = await checkRateLimit(req, userId);
  if (!rateLimitCheck.success) {
    return rateLimitCheck.response!;
  }

  // 2. 正常執行 AI 提煉業務邏輯...
}


🔄 第二步:生產級災難復原與自動重試機制 (src/lib/gemini/resilience.ts)

當 Google AI API 偶發遭遇伺服器過載(HTTP 503 Service Unavailable)或速率限制(HTTP 429 Too Many Requests)時,若直接報錯會嚴重影響使用者體驗。我們實作帶有指數退避(Exponential Backoff)與雜訊抖動(Jitter)的自動重試包裝器:

// src/lib/gemini/resilience.ts
export async function withGeminiResilience<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  initialDelayMs: number = 1000
): Promise<T> {
  let attempt = 0;

  while (true) {
    try {
      return await fn();
    } catch (error: any) {
      attempt++;
      const status = error?.status || error?.statusCode;
      const isTransient = status === 429 || status === 503 || error?.code === 'ECONNRESET';

      if (attempt >= maxRetries || !isTransient) {
        console.error(`[Resilience] 達到最大重試次數 (${attempt}) 或非瞬時錯誤,終止重試。`);
        throw error;
      }

      // 計算指數退避時間加隨機抖動 (Exponential Backoff with Jitter)
      const delay = initialDelayMs * Math.pow(2, attempt - 1) + Math.random() * 500;
      console.warn(`[Resilience Warning] 偵測到瞬時錯誤 (Status: ${status}),將於 ${delay.toFixed(0)}ms 後進行第 ${attempt + 1} 次重試...`);

      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}


🧪 實測驗證:模擬 429 速率限制與自動重試恢復

我們在開發環境中對 Gemini API 發起連續壓測並刻意注入異常:

  1. 壓測觸發:當短時間內並發請求超過 Google AI 預設配額時,API 拋出 429 Too Many Requests。
  2. 重試攔截:withGeminiResilience 捕捉到異常,判定為瞬時錯誤(Transient Error)。
  3. 指數退避:系統自動暫停 1.2 秒後進行第二次發起。
  4. 恢復正常:第二次請求成功獲得回應,前端使用者完全感受不到後端的短暫波動!

🎯 總結與明日預告

今天我們為 OmniVibe AI 補上了走向正式營運最重要的穩定性與防禦裝甲:

  1. 繪製並梳理了涵蓋 Frontend、Cloud Run、Cloud Tasks、Qdrant 與 Supabase 的 全棧生產級架構總覽圖。
  2. 實作了基於 Upstash Redis 的 API Rate Limiting 限流防禦,保護系統免受惡意刷量與費用暴增。
  3. 封裝了具備 Exponential Backoff 指數退避與自動重試機制的 Resilience 容錯包裝器。

歷經 28 天的密集開發與架構迭代,我們的 OmniVibe AI 專案已經完美落地。明天,我們將迎來這趟鐵人賽的最終篇章!

👉 明天(Day 29),我們將進入【完賽總結與未來展望篇】:18 天全棧 AI SaaS 開發心得總結與 Google AI 生態系的未來無限可能!

我們明天見!🔥


上一篇
Day 27 -【Agentic AI 工具調用】Gemini 1.5 Function Calling 實戰:從內容提煉到自動發送 Email 與建立日曆行程
系列文
用 Google AI 生態系 30 天從零打造一個全棧 AI SaaS 服務 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言