Skip to main content

Architecture Journal

JavaSpringBootDistributedSystems

Lucky Draw System

Lucky Draw System 是一套面向電商活動的分散式轉盤抽獎平台。系統把登入、活動、抽獎與庫存拆成獨立服務,讓流量控制、抽獎邏輯與庫存真相各自演進,也能在高併發下維持一致的抽獎結果。

系統架構

平台由四個服務組成,每個服務只操作自己的資料域,跨服務則透過 API 或事件溝通:

  • API Gateway:統一處理 JWT 驗證、限流、冪等 Header 檢查與路由。
  • Auth Service:負責註冊、登入、RS256 JWT 簽發,以及 token 白名單與登出撤銷。
  • Campaign Service:管理活動與獎品,執行權重抽獎、批次抽獎、冪等重播與 Redis 預扣。
  • Inventory Service:作為庫存唯一真相,處理條件扣減、失敗補償與定期帳目校對。

Campaign Service 會透過 RabbitMQ 發送庫存扣減事件,Inventory Service 非同步消費並完成最終扣庫存。這樣可以把抽獎請求的即時回應與後端庫存協作拆開,降低服務之間的直接耦合。

核心設計

Redis 加速,資料庫定案

抽獎流程採用兩段式庫存處理:

  1. Redis Lua script 以原子操作檢查並預扣庫存,讓高併發請求可以快速完成庫存判定。
  2. Inventory Service 使用資料庫條件更新,只在 stock >= qty 時扣減,避免庫存變成負數。
  3. 如果最終扣減失敗,系統會建立補償結果、發出告警,並透過對帳讓 Redis 與資料庫重新收斂。

Redis 負責低延遲與併發控制,資料庫則保留最終真相。當庫存不足時,抽獎直接降級為銘謝惠顧,不重新抽取其他獎品。

三層冪等防線

每次抽獎都必須帶上 Idempotency-Key。同一個請求會依序經過:

  • Redis SETNX 冪等鎖,擋住短時間內的重複請求。
  • 資料庫唯一鍵,確保多個服務實例同時處理時仍不會重複寫入。
  • Replay 機制,重送相同請求時回傳原本的抽獎結果,不重抽、不重扣庫存。

事件驅動的庫存協作

Campaign Service 會發布兩種事件:inventory-commit 用於中獎扣減,prize-stock-configured 用於同步獎品數量變更。事件帶有可重複處理的識別鍵,讓消費端可以安全重試,也方便日後替換訊息基礎設施。

技術棧

  • 語言與框架:Java 21、Spring Boot 3.3、Spring Cloud Gateway、Spring Security
  • 資料庫:開發環境使用 H2,正式環境使用 PostgreSQL 與 Flyway
  • 快取與併發控制:Redis、Lua、冪等鎖、限流計數與 token 白名單
  • 訊息傳遞:Spring Cloud Stream + RabbitMQ,保留切換 Kafka 的彈性
  • 測試與契約:JUnit 5、Testcontainers、OpenAPI 3.0、MapStruct

專案連結

GitHub — jjyung/lucky-draw-system