今天繼續(xù)談一下業(yè)務(wù)層改造相關(guān)的一個(gè)場(chǎng)景:領(lǐng)取紅包朦蕴。紅包,是這幾年最火的一類營(yíng)銷手段刊愚,從當(dāng)年微信紅包走紅后踊跟,無論是電商還是互金,都開始加入了“紅包”百拓。紅包場(chǎng)景本身并不復(fù)雜琴锭,但由于涉及到了資金的轉(zhuǎn)移晰甚,就會(huì)引入事務(wù)一致性的問題,而且從我了解到的情況看决帖,很多同學(xué)在處理這塊業(yè)務(wù)的時(shí)候厕九,剛開始都一不小心犯了錯(cuò),這也是為什么我要選“紅包”場(chǎng)景來展開談的原因地回。
為了盡可能簡(jiǎn)化問題扁远,以“小明領(lǐng)取現(xiàn)金紅包20元”為例,整個(gè)事務(wù)包含如下操作:
檢查20元紅包是否有效
設(shè)置紅包狀態(tài)為“已領(lǐng)取”
小明賬戶余額加上20元
很明顯刻像,A畅买,B,C3個(gè)操作细睡,要么全部成功谷羞,要么全部失敗。但在這個(gè)場(chǎng)景下溜徙,會(huì)有什么潛在的問題呢湃缎?
一, 1包多領(lǐng)蠢壹,多個(gè)線程同時(shí)發(fā)送領(lǐng)取同一個(gè)紅包的操作嗓违,紅包金額重復(fù)添加。這個(gè)問題常見外部惡意攻擊API图贸,由于缺乏必要的數(shù)據(jù)一致性保護(hù)措施蹂季,讀取臟數(shù)據(jù),導(dǎo)致多領(lǐng)疏日。
二偿洁,丟失更新,領(lǐng)取紅包的同時(shí)沟优,同時(shí)進(jìn)行了購(gòu)買操作父能,線程1的事務(wù)覆蓋線程2事務(wù)已經(jīng)提交的數(shù)據(jù),造成線程2事務(wù)所做操作丟失:
在提出解決方案前净神,我們將后端系統(tǒng)架構(gòu)分為兩類:
單機(jī)版 Standalone,數(shù)據(jù)庫相同溉委,所有的業(yè)務(wù)操作在一個(gè)容器下鹃唯。
微服務(wù) 紅包操作,個(gè)人賬戶操作不在同一個(gè)容器內(nèi)進(jìn)行瓣喊,分屬不同的服務(wù)坡慌,擁有各自的數(shù)據(jù)庫。
單機(jī)版的解決方案
如下圖所示藻三,在應(yīng)用代碼中洪橘,對(duì)紅包與賬戶余額都上鎖跪者,確保紅包對(duì)象同時(shí)只會(huì)被一個(gè)線程所操作。對(duì)紅包加鎖熄求,如果有多余線程想操作紅包渣玲,一定需要等待線程1被執(zhí)行結(jié)束,這個(gè)時(shí)候紅包的狀態(tài)已經(jīng)被更新到數(shù)據(jù)庫中弟晚,根本上杜絕一包多領(lǐng)的情況忘衍。對(duì)賬戶余額加鎖,如果余額賬戶在扣款的時(shí)候卿城,確保領(lǐng)取紅包的操作枚钓,不會(huì)讀取臟數(shù)據(jù),造成更新丟失瑟押。
微服務(wù)下的解決方案
微服務(wù)的解決方案引入了消息隊(duì)列來進(jìn)行處理搀捷。如果紅包狀態(tài)正常,并成功將狀態(tài)至為“已領(lǐng)取”多望,且消息已經(jīng)發(fā)送成功嫩舟,用戶服務(wù)端開始消費(fèi)這條消息,如果這個(gè)時(shí)候出現(xiàn)消費(fèi)失敗或者消費(fèi)超時(shí)便斥,利用消息隊(duì)列進(jìn)行重試至壤,直到用戶端執(zhí)行成功,考慮到“一包多領(lǐng)”的問題枢纠,整個(gè)過程中有可能會(huì)出現(xiàn)消息重復(fù)的問題像街。所以我們?cè)谖⒎?wù)中,需要做到以下兩點(diǎn):
1. 消費(fèi)端處理消息的業(yè)務(wù)邏輯保持冪等性晋渺。
2. 保證每條消息都有唯一編號(hào)且保證消息處理成功與去重表的日志同時(shí)出現(xiàn)镰绎。
在使用MQ過程中,需要注意以下幾點(diǎn):
一個(gè)應(yīng)用盡可能用一個(gè) Topic木西,消息子類型用 tags 來標(biāo)識(shí)畴栖。只有發(fā)送消息設(shè)置了tags,消費(fèi)方在訂閱消息時(shí)八千,才可以利用 tags 在 broker 做消息過濾吗讶。
每個(gè)消息在業(yè)務(wù)層面的唯一標(biāo)識(shí)碼,要設(shè)置到 keys 字段恋捆,方便將來定位消息丟失問題照皆。
消息發(fā)送成功或者失敗,要打印消息日志沸停,務(wù)必要打印 sendresult 和 key 字段膜毁。
對(duì)于消息不可丟失應(yīng)用,務(wù)必要有消息重發(fā)機(jī)制。
總結(jié)
在OLTP系統(tǒng)領(lǐng)域瘟滨,我們?cè)诤芏鄻I(yè)務(wù)場(chǎng)景下都會(huì)面臨事務(wù)一致性方面的需求,一個(gè)看起來簡(jiǎn)單的功能候醒,內(nèi)部可能需要調(diào)用多個(gè)“服務(wù)”并操作多個(gè)數(shù)據(jù)庫來實(shí)現(xiàn),特別是高并發(fā)的情況下杂瘸,更是考驗(yàn)我們架構(gòu)中細(xì)節(jié)處理的時(shí)候倒淫。這里,沒有一個(gè)標(biāo)準(zhǔn)的萬能答案胧沫,不過只要我們能遵循基本的設(shè)計(jì)原則昌简,都可以在場(chǎng)景下找到解決方案。