iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0

前言:現有單體架構的侷限

我們目前架構使用的 MemoryRateLimiterStorage 計數器,所有的限流狀態、計次,都存在 JVM 的記憶體中:

private static final ConcurrentHashMap<String, Counter> storage = new ConcurrentHashMap<>();

這在單一 AP Server (Application Server) 的環境下運作正常,但在多 AP 實例的分散式環境下會遇到一些挑戰,如:

  • 狀態隔離:每個 AP 實例都有自己的記憶體空間

    用戶A 在 AP01 已經用完了 5 次的請求額度,但 LoadBalancer 將他下個請求路由到了 AP02,由於 AP02 沒有用戶A 的請求紀錄,所以又會重新開始計次,等於用戶A 最終使用了超出預期的限流次數。

  • 無法水平擴展:如果沒有做分散式的限流置,新增的 AP 實例不會繼承現有的限流狀態,重啟的應用會丟失所有限流紀錄,無法實現統一限流。

解決方案:分散式限流

所以我們接下來要努力的方向,就是如何將現有架構擴展成分散式的系統,讓多個獨立的 AP 實例可以共享統一份限流狀態,因此我們需要一個外部的、持久的、高可用的狀態儲存系統。而這條路上我們要跨越的核心挑戰包括但不限於:

  • 原子性操作:限流的核心操作是「檢查狀態 → 更新狀態 → 判斷限制」,必須確保這幾個步驟的原子性,如果在 A 線程操作過程中,B 線程也同時訪問了共享狀態,就會出現 Race Condition。
  • 網路延遲和可用性:每次限流檢查都需要網路調用,外部存儲可能故障,此時需要降級策略,需要在一致性跟可用性間權衡。
  • 資料過期與清理:分散式環境下誰負責清理資料,如何避免重複清理,如何處理清理過程中的鎖競爭。

Why Redis ?

  • 共享:Redis 是一個獨立的 Server,天然就支援多 Client 端的連接
  • 原子:Redis 提供多種原子性操作,還有分散式鎖
  • 多種資料結構:不同限流算法需要不同的資料結構
    • Fixed Window:簡單計數器 (INCR)
    • Sliding Window:ZSET (ZADD, ZREMRANGEBYSCORE)
    • Token Bucket、Leaky Bucket:Hash
  • 內建 TTL 過期機制
  • 高效:直接訪問記憶體比資料庫快數十倍
    • Pipeline 支援:批次操作減少網路往返
    • ConnectionPool:重複用連線資源降低成本

總結

選擇 Redis 不一定是因為它是「最完美」的解決方案,而是因為它是「最 Balance」的,它在一致性、效能、複雜度之間取得了平衡。明天開始實作 SpringBoot 裡 Redis Client 的基礎配置。


上一篇
Day 6 | 限流器 Lab 實作:搭建 Filter 實作 RequestContext 基礎信息管理
下一篇
Day 8 | 限流器實作:在 SpringBoot 系統中配置 Redis Client
系列文
系統設計一招一式:最基本的功練到爛熟就是殺手鐧,從單體架構到分布式系統的 Lab 實作筆記14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言