這篇文章分享了使用 DTM 二階段消息模式解決?issue #10036?的方法。
今天我們要使用 EasyAbp 的?Abp.EventBus.Boxes.Dtm?模塊。
DTM 事件箱的介紹
這個模塊使用了 DTM 的?二階段消息?使得 ABP 的事件箱得以支持?多租戶多數據庫場景。
你需要先閱讀?DTM 文檔,它將幫助你理解這個模塊。
與 ABP 默認事件箱的差異
DTM 二階段消息事件箱 | ABP 5.0+ 默認事件箱 | |
---|---|---|
收發速度 | ?? | ? |
更少的數據傳輸 | ? | ?? |
保證事件發出 (事務工作單元) | ?? | ?? |
保證事件發出 (非事務工作單元) | ? | ?? (要求消費端解決冪等) |
不要求消費端解決冪等 | ?? | ?? |
支持多租戶多數據庫 | ?? | ? |
沒有增加外部設施 | ? | ?? |
管理面板和報警 | ?? | ? |
DTM 發件箱是如何工作的?
假設你正在使用發件箱發布新的事件:
await _distributedEventBus.PublishAsync(eto1, useOutbox: true);await _distributedEventBus.PublishAsync(eto2, useOutbox: true); // useOutbox 的默認值即 true
DTM 發件箱會臨時存儲這些事件。接下來我們看看,在你完成當前工作單元時它會怎么做:
// UnitOfWork.cs 的代碼片段protected override async Task CommitTransactionsAsync()
{ // 第 1 步:在事務內插入一條記錄到 DTM 屏障表,接著發送一條“prepare”請求到 DTM 服務器await DtmMessageManager.InsertBarriersAndPrepareAsync(EventBag); // 第 2 步: 提交當前 DB 事務await base.CommitTransactionsAsync(); // 第 3 步: 發送一條"submit"請求到 DTM 服務器OnCompleted(async () => await DtmMessageManager.SubmitAsync(EventBag));
}
至此,DTM 服務器已經收到了一條“submit”請求。它會調用 app 的?PublishEvents
?服務并附上所有事件的數據,后者被調用后會立即發布這些事件到 MQ。
如果你依然對這個模式“如何確保發送”困惑,請查看 DTM 的?二階段消息文檔?以了解更多。
DTM 收件箱是如何工作的?
與 ABP 的默認實現不同,DTM 收件箱從 MQ 收到一個事件后會立即處理(handle)。在所有的 handler 完成他們的工作后,收件箱會沿用當前事務,向 DTM 屏障表插入一條記錄。最后它提交了事務并向 MQ 返回 ACK。
所有入箱的事件都擁有一條唯一的 MessageId。擁有相同 MessageId 的事件只會被處理一次,因為我們不能插入 gid (MessageId) 重復的記錄到 DTM 屏障表。
正如你注意到的,DTM 服務器沒有參與收件箱的工作。🤭
安裝和使用
請閱讀?https://github.com/EasyAbp/Abp.EventBus.Boxes.Dtm/tree/main#installation.
后記
這樣的一個 DTM 事件箱實現并不是完美的解決方案。這里最大的成本是,你需要額外關心 DTM 服務器的可用性。但是如果你遇到了多租戶多數據庫的場景,它就是現在最佳的選擇。