結論先講

重寫是最高風險的選項。 一邊維護舊系統一邊寫新系統,兩邊的功能要同步,團隊人力要分成兩半。Strangler Fig 讓你不需要重寫——在 nginx 層做路由切換,把 /api/auth/* 導向新的 Auth Service,其他路由繼續走舊單體。一次搬一塊,零 downtime。


Strangler Fig Pattern

名字來自植物——絞殺榕從宿主樹上發芽,慢慢包圍宿主,最終宿主死亡、絞殺榕成為新的樹幹。

應用到軟體:新服務從舊單體旁邊長出來,一個功能一個功能地接管,最終舊單體被完全替換。

實作方式

# nginx.conf — 路由切換的控制中心
 
upstream monolith {
  server old-app:3000;
}
 
upstream auth-service {
  server auth-service:3001;
}
 
upstream file-service {
  server file-service:3002;
}
 
server {
  # Phase 1: Auth 拆出去
  location /api/auth/ {
    proxy_pass http://auth-service;
  }
 
  # Phase 2: File 拆出去
  location /api/files/ {
    proxy_pass http://file-service;
  }
 
  # 其他全部走舊單體
  location / {
    proxy_pass http://monolith;
  }
}

每拆一個服務,加一行 nginx 配置。不需要改前端的任何 code——前端還是打同一個 domain,nginx 在背後做路由分發。

優點

  1. 零 downtime:改 nginx config + reload,幾毫秒完成
  2. 可以回滾:新服務出問題?把 nginx 路由改回舊單體,10 秒恢復
  3. 漸進式:一次搬一塊,風險可控
  4. 團隊不用分兩半:不是「維護舊 + 開發新」,而是「一次搬一塊」

搬遷順序建議

  1. 第一刀:拆最獨立、最痛的模組(通常是 Auth 或 File)
  2. 第二刀:拆讀取密集的模組(加 Redis cache 效果最好)
  3. 第三刀:拆寫入密集的模組(可能需要 event-driven)
  4. 最後:拆剩下的小模組,關掉舊單體

搬遷期間的資料處理

Phase 1:共用 DB

最簡單的起步方式。新服務和舊單體連同一個 DB。

nginx → Auth Service → DB ←
      → Monolith    → DB ←  同一個 DB

優點:不需要資料同步。缺點:schema 變更要兩邊同步。

Phase 2:API 代理

新服務有自己的 DB,但遷移期間透過 API 存取舊 DB 的資料。

Auth Service → Auth DB(新)
             → GET /api/users/:id → Monolith → 舊 DB

Phase 3:完全分離

每個服務有自己的 DB,服務間用 event 同步資料。

這是最終形態,但需要 event-driven 架構 支撐。不要急著到這一步。


常見的搬遷陷阱

1. 搬遷期間功能凍結

「等新服務寫好了再加功能」——然後新服務寫了 6 個月還沒好,舊系統 6 個月沒更新,客戶跑了。

解法:搬遷和功能開發並行。Strangler Fig 就是為此設計的——舊單體繼續改、新服務慢慢接管。

2. 過早分 DB

共用 DB 的階段很多人急著分——「教科書說每個 service 要有自己的 DB」。

但分 DB 意味著:

  • 跨服務查詢要走 API(原本一個 JOIN 搞定)
  • 資料一致性要靠 event(原本一個 transaction 搞定)
  • 運維要管多個 DB instance

第 28 篇 的經驗:共用 DB 撐了好幾個月才開始分。先拆 process 再分 DB。

3. 忘記測試搬遷後的效能

每拆一個服務,跑一次壓測(k6 腳本 5 分鐘就寫好)。確認:

  • 新服務的效能 ≥ 舊單體中的相同功能
  • nginx 路由沒有引入額外延遲
  • 跨服務呼叫的 latency 可接受

下一篇

Docker Compose vs K8s:1000 人分水嶺 — 拆完微服務要部署到哪裡?壓測顯示 k3s 在 100 人以上就大量報錯,Docker Compose 500 人零錯誤。K8s 的價值在自動化運維,不在效能。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:微服務拆分時機:什麼 UV 該拆、什麼時候不該拆 → 下一篇:Docker Compose vs K8s:1000 人是分水嶺