java消息队列mq_我爱java系列---【消息队列(rabbitmq)】
使用消息隊(duì)列來避免分布式事務(wù)
如果仔細(xì)觀察生活的話,生活的很多場(chǎng)景已經(jīng)給了我們提示。
比如在北京很有名的姚記炒肝點(diǎn)了炒肝并付了錢后,他們并不會(huì)直接把你點(diǎn)的炒肝給你,往往是給你一張小票,然后讓你拿著小票到出貨區(qū)排隊(duì)去取。
為什么他們要將付錢和取貨兩個(gè)動(dòng)作分開呢?原因很多,其中一個(gè)很重要的原因是為了使他們接待能力增強(qiáng)(并發(fā)量更高)。
還是回到我們的問題,只要這張小票在,你最終是能拿到炒肝的。同理轉(zhuǎn)賬服務(wù)也是如此,當(dāng)用戶A賬戶扣除1萬后,
我們只要生成一個(gè)憑證(消息)即可,這個(gè)憑證(消息)上寫著“讓用戶B賬戶增加 1萬”,只要這個(gè)憑證(消息)能可靠保存,
我們最終是可以拿著這個(gè)憑證(消息)讓用戶B賬戶增加1萬的,即我們能依靠這個(gè)憑證(消息)完成最終一致性。
1 如何可靠保存憑證(消息)
有兩種方法:
1.1 業(yè)務(wù)與消息耦合的方式
用戶A在完成扣款的同時(shí),同時(shí)記錄消息數(shù)據(jù),這個(gè)消息數(shù)據(jù)與業(yè)務(wù)數(shù)據(jù)保存在同一數(shù)據(jù)庫實(shí)例里(消息記錄表表名為message);
上述事務(wù)能保證只要用戶A賬戶里被扣了錢,消息一定能保存下來。
當(dāng)上述事務(wù)提交成功后,我們通過實(shí)時(shí)消息服務(wù)將此消息通知用戶B,用戶B處理成功后發(fā)送回復(fù)成功消息,用戶A收到回復(fù)后刪除該條消息數(shù)據(jù)。
1.2 業(yè)務(wù)與消息解耦方式
上述保存消息的方式使得消息數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù)緊耦合在一起,從架構(gòu)上看不夠優(yōu)雅,而且容易誘發(fā)其他問題。為了解耦,可以采用以下方式。
1)用戶A在扣款事務(wù)提交之前,向?qū)崟r(shí)消息服務(wù)請(qǐng)求發(fā)送消息,實(shí)時(shí)消息服務(wù)只記錄消息數(shù)據(jù),而不真正發(fā)送,只有消息發(fā)送成功后才會(huì)提交事務(wù);
2)當(dāng)用戶A扣款事務(wù)被提交成功后,向?qū)崟r(shí)消息服務(wù)確認(rèn)發(fā)送。只有在得到確認(rèn)發(fā)送指令后,實(shí)時(shí)消息服務(wù)才真正發(fā)送該消息;
3)當(dāng)用戶A扣款事務(wù)提交失敗回滾后,向?qū)崟r(shí)消息服務(wù)取消發(fā)送。在得到取消發(fā)送指令后,該消息將不會(huì)被發(fā)送;
4)對(duì)于那些未確認(rèn)的消息或者取消的消息,需要有一個(gè)消息狀態(tài)確認(rèn)系統(tǒng)定時(shí)去用戶A系統(tǒng)查詢這個(gè)消息的狀態(tài)并進(jìn)行更新。為什么需要這一步驟,
舉個(gè)例子:假設(shè)在第2步用戶A扣款事務(wù)被成功提交后,系統(tǒng)掛了,此時(shí)消息狀態(tài)并未被更新為“確認(rèn)發(fā)送”,從而導(dǎo)致消息不能被發(fā)送。
優(yōu)點(diǎn):消息數(shù)據(jù)獨(dú)立存儲(chǔ),降低業(yè)務(wù)系統(tǒng)與消息系統(tǒng)間的耦合;
缺點(diǎn):一次消息發(fā)送需要兩次請(qǐng)求;業(yè)務(wù)處理服務(wù)需要實(shí)現(xiàn)消息狀態(tài)回查接口。
2 如何解決消息重復(fù)投遞的問題
還有一個(gè)很嚴(yán)重的問題就是消息重復(fù)投遞,以我們用戶A轉(zhuǎn)賬到用戶B為例,如果相同的消息被重復(fù)投遞兩次,那么我們用戶B賬戶將會(huì)增加2萬而不是1萬了。
為什么相同的消息會(huì)被重復(fù)投遞?比如用戶B處理完消息msg后,發(fā)送了處理成功的消息給用戶A,正常情況下用戶A應(yīng)該要?jiǎng)h除消息msg,但如果用戶A這時(shí)候悲劇的掛了,
重啟后一看消息msg還在,就會(huì)繼續(xù)發(fā)送消息msg。
解決方法很簡單,在用戶B這邊增加消息應(yīng)用狀態(tài)表(message_apply),通俗來說就是個(gè)賬本,用于記錄消息的消費(fèi)情況,每次來一個(gè)消息,
在真正執(zhí)行之前,先去消息應(yīng)用狀態(tài)表中查詢一遍,如果找到說明是重復(fù)消息,丟棄即可,如果沒找到才執(zhí)行,同時(shí)插入到消息應(yīng)用狀態(tài)表(同一事務(wù))
總結(jié)
以上是生活随笔為你收集整理的java消息队列mq_我爱java系列---【消息队列(rabbitmq)】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python如何实现找图_python实
- 下一篇: 手游堡垒之夜服务器没响应,堡垒之夜国际服