在系統(tǒng)服務(wù)化的過程中,我們不得不面臨的一個問題是多個子系統(tǒng)間業(yè)務(wù)數(shù)據(jù)的一致性如何保證歹鱼,解決這個問題有多種方式。
XA
可能很多人首先會想到XA規(guī)范中定義的分布式事務(wù),下圖是XA規(guī)范中定義的DTP(Distributed Transaction Processing)模型:
但該模式并不適用于如今的互聯(lián)網(wǎng)應(yīng)用绿语,主要有以下幾點問題:
- XA采用2PC,并不能完全保證一致性候址;
- 2PC有同步阻塞問題吕粹,并且增加了2次RTTs(round trip times),性能低下岗仑;
- 服務(wù)化架構(gòu)天然與DTP模型相悖匹耕,服務(wù)化架構(gòu)顯然是面向服務(wù)的,為了解決數(shù)據(jù)一致性荠雕,直接向?qū)Ψ奖┞稊?shù)據(jù)庫稳其,這還是服務(wù)化嗎?
本地消息表+MQ
既然分布式事務(wù)不行炸卑,我們自然想到了本地事務(wù)既鞠,可以利用本地事務(wù)的ACID特性來保證一致性,數(shù)據(jù)存儲之后矾兜,再想辦法將數(shù)據(jù)傳輸給其它系統(tǒng)损趋,做之后的業(yè)務(wù)處理,如果處理失敗椅寺,還可以重試浑槽。這個過程實現(xiàn)起來并不難,并且有多種實現(xiàn)方式返帕,但每個系統(tǒng)自己去搞成本還是很高的桐玻,所以我們追求的是一種通用且相對優(yōu)雅的方式。目前業(yè)界多數(shù)都采用本地消息表+MQ的方式荆萤,本地消息表設(shè)計成一種通用的結(jié)構(gòu)镊靴,與業(yè)務(wù)模型無關(guān),將其放到業(yè)務(wù)庫同實例上链韭,這樣就可以保證業(yè)務(wù)操作和存儲消息在同一個事物內(nèi)偏竟,其它線程讀取消息表數(shù)據(jù),發(fā)送到MQ Server(broker)敞峭,發(fā)送成功則刪除數(shù)據(jù)踊谋,失敗則保留數(shù)據(jù),后續(xù)會不斷重試旋讹,直至成功殖蚕。隨后MQ Server進(jìn)行廣播通知其它系統(tǒng)轿衔,做后續(xù)處理,如果處理失敗睦疫,MQ Server負(fù)責(zé)重試害驹。
上面的流程看起來對業(yè)務(wù)侵入也比較大,但我們可以通過一些手段來屏蔽這些細(xì)節(jié):
- 首先蛤育,我們要搭建一個公共的MQ Server宛官;
- 其次,我們要預(yù)先在業(yè)務(wù)庫去建立一個消息表缨伊,可以通過運維的手段解決摘刑;
- 最后进宝,我們要提供一個拆箱即用的Message Producer API刻坊,屏蔽發(fā)送消息的細(xì)節(jié),Message Consumer API直接用MQ原生的即可党晋。
@Transactional
public void doSth(Object obj) {
xxxDao.insert(obj);// 1.biz operation
Message msg = buildMessage(obj);
messageProducer.sendMessage(msg);// 2.store msg
}
這套機制從0搭建起來還是需要較大成本的谭胚,但搭建完之后將一勞永逸,算是先苦后甜吧未玻。
RocketMQ
那有沒有現(xiàn)成的解決方案呢灾而?答案是有的,那就是今年風(fēng)頭很盛的RocketMQ(已成為Apache頂級項目)扳剿,RocketMQ在其商業(yè)版中提供了事物型消息旁趟,所謂事物型消息就是保證了業(yè)務(wù)操作和發(fā)送消息滿足一致性(業(yè)務(wù)操作成功,消息一定發(fā)送成功庇绽,業(yè)務(wù)操作失敗锡搜,消息一定未發(fā)送,反之也成立)瞧掺,上面描述的方式(本地消息表+MQ)就可以看作是事物型消息耕餐,下面引用一下阿里中間件團(tuán)隊博客中的描述。
這里的事務(wù)消息指的其實是數(shù)據(jù)發(fā)送者事務(wù)消息辟狈,簡單而言就是在真正地做業(yè)務(wù)邏輯之前會發(fā)送一條半消息到服務(wù)端肠缔,接下來發(fā)送者會執(zhí)行本地的事務(wù),在完成本地事務(wù)之后哼转,如果成功就會向服務(wù)端發(fā)送一條確認(rèn)信息明未,這時候服務(wù)端會將之前的半消息事務(wù)狀態(tài)進(jìn)行變更;如果失敗了壹蔓,服務(wù)端就會不斷地回調(diào)客戶端趟妥,來保證發(fā)送端的事務(wù)一致性。
http://jm.taobao.org/2017/03/09/20170309/
這里的關(guān)鍵點是第三步如果失敗庶溶,要依靠第四步broker回調(diào)客戶端來保證一致性煮纵,這里猜測一下其大概實現(xiàn)懂鸵,客戶端發(fā)送消息前,會添加一個事物檢查的實現(xiàn)行疏,并開通一個端口匆光,broker回調(diào)時會攜帶著topic、message等信息酿联,客戶端依據(jù)message信息和自己的業(yè)務(wù)數(shù)據(jù)判定是提交或者回滾终息,可能有些業(yè)務(wù)場景依據(jù)這兩個信息無法判定,那客戶端依然要建立本地消息表贞让。
其它方案
其它方案大多缺失普適性周崭,比如TCC(Try/Confirm/Cancel),適用于金融領(lǐng)域喳张。
版權(quán)聲明
本博客所有的原創(chuàng)文章续镇,作者皆保留版權(quán)。轉(zhuǎn)載必須包含本聲明销部,保持本文完整摸航,并以超鏈接形式注明作者高爽和本文原始地址:http://www.reibang.com/p/8a1c14500ade。