秒殺的分佈式事務是如何設計的?
0:一圖解讀分佈式事務
首先奉上一張全網最爲牛逼的圖,給大家做個總覽:
1:Seata 和 Rocketmq 事務消息實現,實現 強弱結合型事務
秒殺 / 搶購 等場景,都是屬於 強弱結合型 的數據一致性場景。
首先看看 RocketMQ 的事務消息, 能夠保證本地操作 + 消息發送的原子性。
具體來說, 主要是保證了本地方法執行和消息發送在一個分佈式事務中,要不全部成功,要不全部失敗。
見下圖:
RocketMQ 通過發送 half 消息來實現,下面詳細說明一下:
-
消息發送方 向 Broker 發送一條 half 消息;
-
half 消息發送成功後,消息發送方 執行本地事務;
-
如果 消息發送方 執行本地事務成功,則向 Broker 發送 commit 請求,否則發送 rollback 請求;
-
如果 Broker 收到的是 rollback 請求,則刪除保存的 half 消息;
-
如果 Broker 收到的是 commit 請求,則把 half 消息投遞到 真實 隊列, 等待消費服務來拉取,然後刪除保存的 half 消息;
-
如果 Broker 沒有收到 rollback/commit 請求,則會發送請求到 Producer 查詢本地事務狀態,然後根據 Producer 返回的本地狀態做 commit/rollback 相關處理。
Seata + Rocketmq 事務消息 結合
Seata + Rocketmq 事務消息 結合的目標:
-
一 是保證 分佈式事務 + 消息 發送的原子性。
-
二 是 再通過 mq 的重試機制,去保證訂閱者的最終一致性,
Seata 的改進主要在 prepare 階段。
Seata 提供了一個 SeataMQProducer 類,把 RocketMQ 中 TransactionListener 的方法加入到全局事務。
見下面代碼:
SeataMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {
super(namespace, producerGroup, rpcHook);
this.transactionListener = new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
return LocalTransactionState.UNKNOW;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
String xid = msg.getProperty(PROPERTY_SEATA_XID);
if (StringUtils.isBlank(xid)) {
LOGGER.error("msg has no xid, msgTransactionId: {}, msg will be rollback", msg.getTransactionId());
return LocalTransactionState.ROLLBACK_MESSAGE;
}
GlobalStatus globalStatus = DefaultResourceManager.get().getGlobalStatus(SeataMQProducerFactory.ROCKET_BRANCH_TYPE, xid);
if (COMMIT_STATUSES.contains(globalStatus)) {
return LocalTransactionState.COMMIT_MESSAGE;
} else if (ROLLBACK_STATUSES.contains(globalStatus) || GlobalStatus.isOnePhaseTimeout(globalStatus)) {
return LocalTransactionState.ROLLBACK_MESSAGE;
} else if (GlobalStatus.Finished.equals(globalStatus)) {
LOGGER.error("global transaction finished, msg will be rollback, xid: {}", xid);
return LocalTransactionState.ROLLBACK_MESSAGE;
}
return LocalTransactionState.UNKNOW;
}
};
在 prepare 階段,SeataMQProducer 向 RocketMQ Broker 發送 half 消息,執行本地事務,如果執行成功,則強行把 LocalTransactionState 改回 UNKNOW,等待 TC 發送指令,決定是 commit 或 rollback。
如果執行失敗,返回 ROLLBACK_MESSAGE,TC 下發指令,回滾全局事務。
Seata + Rocketmq 事務消息 結合的使用場景
如果 MQ Broker 沒有收到 commit/rollback 消息,則會回查 Producer 本地事務狀態,也就是上面代碼中的 checkLocalTransaction。
checkLocalTransaction 檢查 全局事務狀態,使用 XID 去查詢 全局事務,去決定 half 消息 是 拋棄還是 投遞 。
集成 RocketMQ 之後,Seata 的分佈式事務調用流程, 下面以 訂單服務、庫存服務兩個服務爲例:
Apache Seata 引入 RocketMQ 後,支持的分佈式事務場景更加豐富,使得 Seata 可以用於 強一致性 + 弱一致性 結合的場景。
2. 面試題標準答案: 如何解決分佈式事務問題的?
現在 Java 面試,分佈式系統、分佈式事務幾乎是標配。而分佈式系統、分佈式事務本身比較複雜,大家學起來也非常頭疼。
面試題:分佈式事務瞭解嗎?你們是如何解決分佈式事務問題的?
Seata AT/TCC 和 MQ 異步確保型 事務 是在生產中最常用。
-
強一致性模型, Seata AT/TCC 強一致方案 模式用於強一致主要用於核心模塊,例如交易 / 訂單等。
-
弱一致性模型。 MQ 異步確保型 事務 弱一致方案一般用於邊緣模塊例如庫存,通過 MQ 原子消息和發佈訂閱,保證最終一致性,也可以業務解耦。
面試中如果你真的被問到,可以分場景回答:
(1)強一致性場景
對於那些特別嚴格的場景,用的是 Seata AT 模式來保證強一致性;
準備好例子:你找一個嚴格要求數據絕對不能錯的場景(如電商交易交易中的庫存和訂單、優惠券),可以回答使用成熟的如中間件 Seata AT 模式。
阿里開源了分佈式事務框架seata經歷過阿里生產環境大量考驗的框架。seata支持Dubbo,Spring Cloud。
是 Seata AT/TCC 模式,保障強一致性,支持跨多個庫修改數據;
-
訂單庫:增加訂單
-
商品庫:扣減庫存
-
優惠券庫:預扣優惠券
(2)弱一致性場景
基於可靠消息的最終一致性,各個子事務可以較長時間內異步,但數據絕對不能丟的場景。可以使用異步確保型事務事。
可以使用基於 MQ 的異步確保型事務,比如電商平臺的通知支付結果:
-
積分服務:增加積分
-
會計服務:生成會計記錄
(3)強弱結合一致性場景
兩階段 提交,如 Seata AT/TCC 模式,保障強一致性,支持跨多個庫修改數據;
-
訂單庫:增加訂單
-
商品庫:扣減庫存
-
優惠券庫:預扣優惠券
異步確保型事務,保障弱一致性,支持跨多個服務和系統修改數據,在下面的場景中相關的弱一致性操作爲:
-
積分服務:增加積分
-
通知服務:發生通知
弱一致性部分:如果不是嚴格對數據一致性要求、或者由不同系統執行子事務的場景,如電商發送成功支付成功消息,只需要保障弱一致性即可。
Seata 新功能: 和 Rocketmq 事務消息實現,實現 強弱結合型事務 , 具體參見下面的文章:
具體的內容,請參見 尼恩 的最新 文章:
文章 1:'分佈式事務' 聖經:從入門到精通,架構師尼恩最新、最全詳解 (50 + 圖文 4 萬字全面總結)
文章 2:最新 Seata 集成了 RocketMQ 事務消息,Seata 越來越 牛 X 了!yyds !
各大模式的總體對比:
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/I17cBMO58Ecw4wCr8HX89w