Skip to content

第十四章:微服務架構設計 (Microservices Architecture)

14.1 引言:單體 vs 微服務

在軟體架構的演進中,我們通常從 模組化單體 (Modular Monolith) 開始。這也是 ABP 官方推薦的起點。然而,當系統規模擴大到一定程度,或者團隊規模超過 20 人時,微服務架構就成為了必要的選擇。

1. 微服務的優勢

  • 獨立部署:修改一個服務不需要重新部署整個系統。
  • 技術多樣性:不同的服務可以使用不同的技術堆疊 (例如 .NET, Node.js, Python)。
  • 彈性擴展:可以針對瓶頸服務單獨擴展 (Scale Out)。
  • 故障隔離:一個服務掛掉不會拖垮整個系統 (前提是做好了隔離)。

2. 微服務的代價

  • 運維複雜度:需要管理更多的伺服器、容器與網路設定。
  • 資料一致性:沒有了全域的資料庫交易,資料一致性變得極其困難。
  • 除錯困難:請求跨越多個服務,追蹤問題需要分散式追蹤工具。

14.2 服務邊界劃分 (Service Boundaries)

劃分微服務的邊界是一門藝術。

1. 依據業務能力 (Business Capabilities)

例如:IdentityService (身分認證), ProductService (商品管理), OrderingService (訂單處理), PaymentService (支付)。

2. 依據子領域 (Subdomains)

DDD 中的 Bounded Context (邊界上下文) 是劃分微服務的最佳依據。通常一個 Bounded Context 對應一個微服務。

3. 資料庫擁有權

黃金法則:每個微服務必須擁有自己的資料庫 (Database per Service)。

  • 嚴禁跨服務直接存取資料庫。
  • 若需要其他服務的資料,必須透過 API 或事件同步。

14.3 服務間通訊 (Inter-Service Communication)

1. 同步通訊 (Synchronous)

使用 HTTP (REST)gRPC

  • 適用場景:查詢 (Query)、需要立即回應的操作。
  • 缺點:服務間強耦合,若下游服務掛掉,上游也會受影響。
  • ABP 支援:使用 HttpApi.Client 代理,像呼叫本地方法一樣呼叫遠端服務。
csharp
public class OrderAppService : ApplicationService
{
    private readonly IProductAppService _productAppService; // 遠端代理

    public async Task CreateOrderAsync(CreateOrderDto input)
    {
        // 同步呼叫 ProductService
        var product = await _productAppService.GetAsync(input.ProductId);
        // ...
    }
}

2. 非同步通訊 (Asynchronous)

使用 Message Bus (RabbitMQ, Kafka)

  • 適用場景:命令 (Command)、狀態變更通知、長執行時間的任務。
  • 優點:解耦、削峰填谷。
  • ABP 支援IDistributedEventBus
csharp
// OrderService
await _eventBus.PublishAsync(new OrderCreatedEto { OrderId = order.Id });

// InventoryService (訂閱者)
public class InventoryHandler : IDistributedEventHandler<OrderCreatedEto>
{
    public async Task HandleEventAsync(OrderCreatedEto eventData)
    {
        // 扣減庫存
    }
}

3. 事件可靠性 (Event Reliability)

ABP 實作了 OutboxInbox 模式來確保事件不丟失。

  • Outbox: 事件先寫入本地資料庫,再由背景工作發送。
  • Inbox: 接收到的事件先寫入本地資料庫,再由背景工作處理。
  • V10 更新: InboxProcessor 新增了 失敗重試策略,可配置重試次數與間隔,避免因暫時性錯誤導致事件遺失。

14.4 分散式交易 (Distributed Transactions)

在微服務中,我們無法使用 ACID 交易。我們必須依賴 Saga 模式 來實現最終一致性。

1. 什麼是 Saga?

Saga 是一系列本地交易的序列。如果其中一個失敗了,Saga 會執行一系列的 補償交易 (Compensating Transactions) 來復原變更。

2. 實作方式:基於事件的編排 (Choreography)

  • 步驟 1OrderService 建立訂單 (Pending),發布 OrderCreated 事件。
  • 步驟 2InventoryService 收到事件,扣減庫存。
    • 若成功:發布 InventoryReserved 事件。
    • 若失敗 (庫存不足):發布 InventoryFailed 事件。
  • 步驟 3PaymentService 收到 InventoryReserved,進行扣款。
    • 若成功:發布 PaymentCompleted
    • 若失敗:發布 PaymentFailed
  • 步驟 4OrderService 根據收到的事件更新訂單狀態。
    • 收到 PaymentCompleted -> 訂單狀態改為 Completed
    • 收到 InventoryFailedPaymentFailed -> 訂單狀態改為 Cancelled (並可能觸發退款)。

14.5 API Gateway

API Gateway 是微服務系統的單一入口。它負責路由、認證、限流與聚合。

1. YARP (Yet Another Reverse Proxy)

微軟官方的高效能反向代理,ABP 微服務範本預設使用 YARP。

2. 配置範例 (appsettings.json)

json
"ReverseProxy": {
  "Routes": {
    "product-route": {
      "ClusterId": "product-cluster",
      "Match": { "Path": "/api/product-service/{**catch-all}" }
    }
  },
  "Clusters": {
    "product-cluster": {
      "Destinations": {
        "destination1": { "Address": "http://localhost:5001" }
      }
    }
  }
}

14.6 可觀測性 (Observability)

微服務若沒有監控就是黑盒子。

1. 分散式追蹤 (Distributed Tracing)

使用 OpenTelemetry 標準。

  • 每個請求都會被標記一個 TraceId
  • 這個 ID 會隨著 HTTP Header 或 Message Bus 傳遞到所有服務。
  • 工具:Jaeger, Zipkin, Aspire Dashboard (新)。

2. 集中式日誌 (Centralized Logging)

使用 Serilog 將日誌寫入 ElasticsearchSeq

  • 透過 TraceId 可以在日誌系統中串聯出完整的請求路徑。

14.7 實戰練習

練習 1:建立微服務方案

  1. 使用 ABP CLI 建立微服務方案 (注意:社群版沒有微服務範本,需手動建立多個 app 專案並配置)。
    • 提示:建立 ProductServiceOrderService 兩個獨立專案。
  2. 配置 RabbitMQ 作為分散式事件匯流排。

練習 2:實作跨服務通訊

  1. ProductService 中建立商品。
  2. OrderService 中建立訂單時,透過 HttpApi.Client 同步檢查商品是否存在。
  3. 訂單建立後,發布事件通知 ProductService 扣減庫存。

練習 3:配置 YARP Gateway

  1. 建立一個新的 ASP.NET Core 空專案作為 Gateway。
  2. 安裝 Yarp.ReverseProxy
  3. 配置路由將 /api/products 轉發到 ProductService,將 /api/orders 轉發到 OrderService

14.8 總結

微服務架構強大但也複雜。

  • 邊界劃分 是最關鍵的決策。
  • 非同步通訊 是解耦的核心。
  • Saga 模式 解決了一致性難題。
  • 可觀測性 是運維的基石。

在下一章,我們將探討如何透過 模組化開發 (Modular Development) 來構建可重用的業務單元,這是微服務與單體架構都適用的重要技術。


參考資源

Released under the MIT License.