iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
Software Development

事件驅動電力交易平台:Spring Boot 實戰系列 第 16

Day 15|多節點不搶單:Queue 分工 × Lua 原子更新的雙保險

  • 分享至 

  • xImage
  •  

1. 多節點風險在哪

當我把撮合引擎部屬到k8s上,水平擴張成多個節點時,最怕的是「同一張對手單被不同節點同時吃走」。這會導致重複成交或扣到負數。為了避免這種搶單,我在「事件層」與「資料層」各做一層保護。

2. 第一層:RabbitMQ 的工作分配語義

order.created.queue 採 work-queue 模式:每一筆 order.created 只會送達其中一個消費者(節點)。
這代表「同一張 taker 訂單」只會有一個節點在處理,天然降低衝突機率。

@RabbitListener(queues = "order.created.queue")
public void handleConfirmedOrder(OrderCreatedEvent event) { ... }

3. 第二層:Redis × Lua 的臨界區原子化

真正會造成衝突的是「從對手簿取出 maker 的那一刻」。我把 取出最優一筆 + 立即移除 打包在 一支 Lua 中執行,Redis 會以單執行緒完成這個指令:

  • 不會被別的節點插隊拿到同一筆 maker;
  • 也不需要額外引入分散式鎖,維護成本更低。

4. 水平擴張的路線

  • 現階段:多節點共同消費同一條 order.created.queue,靠 Lua 原子區保證一致。
  • 未來演進:若交易品項(symbol)很多、量更大,可以把 routing key 帶上 order.created.{sym},以 symbol 維度分片到不同 queue,做到「每個 symbol 單消費者」的更強隔離。這會讓整體吞吐量更好,對長尾品項也更友善。

5. 公平性與可追溯

  • 我在 OrderMatchedEvent 帶有成交時間與雙方原始報價,order 服務可以可靠落帳;
  • 若未來要嚴格同價 FIFO,我會把時間維度引入排序(或改為 ZSET + LIST 的雙層結構),確保公平性與可重播。

上一篇
Day 15|部分/全部成交與撤單:回簿、移除與 REST 控制
系列文
事件驅動電力交易平台:Spring Boot 實戰16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言