API Gateway 模式

是什麼?

API Gateway 是微服務架構中的反向代理(Reverse Proxy),作為所有 Client 請求的單一入口點,負責將請求路由到對應的後端服務,同時處理橫切關注點(Cross-Cutting Concerns)。

ℹ️常見 API Gateway 產品

雲端:AWS API Gateway、Azure API Management、GCP API Gateway。開源:Kong、APISIX、Envoy。框架內建:Ocelot(.NET)、Spring Cloud Gateway(Java)。

核心觀念

API Gateway 的核心職責

| 職責 | 說明 | |------|------| | 路由(Routing) | 將請求轉發到對應的微服務 | | 認證與授權 | 統一驗證 JWT/API Key,避免每個服務重複實作 | | 限流(Rate Limiting) | 防止單一 client 過度使用資源 | | 負載均衡 | 在同一服務的多個 instance 間分配請求 | | 回應聚合 | 將多個微服務的回應組合成一個回傳給 client | | 協定轉換 | 對外 REST、對內 gRPC | | 快取 | 快取頻繁請求的回應,降低後端壓力 | | 日誌與監控 | 集中記錄請求日誌和 metrics |

BFF(Backend for Frontend)模式

不同的 client(Web、Mobile、IoT)需要不同的資料格式和欄位。BFF 模式為每種 client 建立專用的 API Gateway:

單點故障風險

API Gateway 是所有請求的必經之路,一旦它掛掉,整個系統都不可用。必須做到:高可用部署(至少 2 個 instance)、健康檢查、自動擴展、graceful degradation。

常見誤區

⚠️常犯錯誤

  • 把業務邏輯放進 API Gateway(它只該處理橫切關注點)
  • API Gateway 成為「上帝服務」(所有邏輯都塞在裡面)
  • 沒有做限流和熔斷(一個服務 timeout 拖垮整個 gateway)
  • 所有微服務共用一個 Gateway 規則(不同服務的認證和限流策略不同)

執行流程

1

Client 發送請求

請求送到 API Gateway 的統一端點

2

認證與授權

驗證 JWT/API Key,檢查權限

3

限流檢查

確認請求沒有超過 rate limit

4

路由轉發

根據 URL path 或 header 轉發到對應的微服務

5

回應處理

聚合、快取、轉換格式後回傳給 client

流程解讀:API Gateway 是一條嚴格的處理管線。每個請求先經過認證(誰在請求?)、授權(有沒有權限?)、限流(有沒有超量?),全部通過後才路由到後端服務。回應回來後可能需要聚合多個服務的資料、轉換格式、設定快取 header。這個管線的順序很重要 — 認證失敗就不需要浪費後端資源。

程式碼範例

C# 版本

csharp
// Ocelot Gateway 設定(ocelot.json)
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/orders/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        { "Host": "order-service", "Port": 80 }
      ],
      "UpstreamPathTemplate": "/api/orders/{everything}",
      "UpstreamHttpMethod": [ "GET", "POST" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Bearer"
      },
      "RateLimitOptions": {
        "ClientWhitelist": [],
        "EnableRateLimiting": true,
        "Period": "1m",
        "Limit": 100
      }
    }
  ]
}
 
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOcelot();
builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://auth.myapp.com";
        options.Audience = "api";
    });
 
var app = builder.Build();
await app.UseOcelot();
app.Run();

TypeScript 版本

typescript
// Express Gateway with custom middleware
import express from "express";
import { createProxyMiddleware } from "http-proxy-middleware";
import rateLimit from "express-rate-limit";
 
const app = express();
 
// Rate Limiting
const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 分鐘
  max: 100,
  standardHeaders: true,
});
app.use(limiter);
 
// JWT 認證 middleware
app.use(async (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ error: "Unauthorized" });
  try {
    req.user = await verifyJwt(token);
    next();
  } catch {
    res.status(401).json({ error: "Invalid token" });
  }
});
 
// 路由到微服務
app.use("/api/orders", createProxyMiddleware({
  target: "http://order-service:3001",
  changeOrigin: true,
}));
 
app.use("/api/payments", createProxyMiddleware({
  target: "http://payment-service:3002",
  changeOrigin: true,
}));
 
// 回應聚合(BFF 模式)
app.get("/api/dashboard", async (req, res) => {
  const [orders, stats] = await Promise.all([
    fetch("http://order-service:3001/api/orders/recent").then(r => r.json()),
    fetch("http://analytics-service:3003/api/stats").then(r => r.json()),
  ]);
  res.json({ orders, stats });
});
 
app.listen(3000);

Python 版本

python
# FastAPI Gateway
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import httpx
from slowapi import Limiter
from slowapi.util import get_remote_address
 
app = FastAPI()
limiter = Limiter(key_func=get_remote_address)
 
# CORS
app.add_middleware(CORSMiddleware,
    allow_origins=["https://myapp.com"],
    allow_methods=["*"], allow_headers=["*"])
 
# 路由表
SERVICE_MAP = {
    "/api/orders": "http://order-service:8001",
    "/api/payments": "http://payment-service:8002",
    "/api/inventory": "http://inventory-service:8003",
}
 
@app.api_route("/api/{service}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
@limiter.limit("100/minute")
async def gateway(request: Request, service: str, path: str):
    target = SERVICE_MAP.get(f"/api/{service}")
    if not target:
        raise HTTPException(status_code=404, detail="Service not found")
 
    # 轉發請求
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method=request.method,
            url=f"{target}/api/{service}/{path}",
            headers=dict(request.headers),
            content=await request.body(),
            timeout=5.0,
        )
    return Response(content=response.content,
                    status_code=response.status_code,
                    headers=dict(response.headers))

結構圖

Web Client
HTTPS
Mobile Client
HTTPS
API Gateway
verify token
Auth Middleware
Rate Limiter
Order Service
Payment Service
User Service

圖中 Web 和 Mobile Client 都透過 API Gateway 這個統一入口存取服務。Gateway 內部先經過 Auth Middleware 驗證身份,再由 Rate Limiter 檢查限流,最後根據 URL 路由到對應的微服務。Client 完全不需要知道後端有幾個服務、在哪裡執行。

面試常見問題

Q: API Gateway 和 Load Balancer 有什麼差別?

A: Load Balancer 工作在 L4/L7 層,只負責分配流量到多個 server instance。API Gateway 工作在 L7 層,除了路由外還處理認證、限流、回應聚合、協定轉換等應用層邏輯。API Gateway 可以搭配 Load Balancer 使用(Gateway 自身也需要多 instance 和負載均衡)。

Q: 什麼是 BFF 模式?什麼時候需要?

A: BFF(Backend for Frontend)為不同的 client 類型建立專用的 API 層。當 Web 需要完整資料而 Mobile 只需要精簡版,或者不同 client 需要不同的認證方式時,BFF 避免了「一個 API 要滿足所有 client」的困境。缺點是維護多個 BFF 的成本。

Q: 如何避免 API Gateway 成為效能瓶頸?

A: 水平擴展(多個 Gateway instance + Load Balancer)、非同步非阻塞 I/O(如 Envoy、Kong 使用非同步架構)、回應快取(減少後端呼叫)、Connection Pooling(重用與後端的連線)、避免在 Gateway 做複雜的業務邏輯。

理解測驗

🤔 以下哪個不是 API Gateway 的職責?

🤔 BFF 模式要解決的問題是什麼?

🤔 API Gateway 的最大風險是什麼?

重點整理

💡一句話記住

API Gateway = 微服務的大門 + 保全 + 服務台。 口訣:「一個入口管路由,認證限流全在此」

| 概念 | 說明 | |------|------| | API Gateway | 所有請求的統一入口和反向代理 | | 路由 | 根據 URL/Header 轉發到對應服務 | | BFF | 為不同 client 建立專用的 API 層 | | 橫切關注點 | 認證、限流、日誌等所有服務共需的功能 | | 核心風險 | 單點故障,必須高可用部署 |

你可能也想看

Service CommunicationSaga Pattern

按 ← → 鍵切換課程