用上 RocketMQ,系統(tǒng)性能提升了 10 倍!

Java技術(shù)棧
www.javastack.cn
關(guān)注閱讀更多優(yōu)質(zhì)文章
我們的訂單系統(tǒng)的邏輯架構(gòu)圖下:

上述架構(gòu)存在如下問(wèn)題:
同步調(diào)用問(wèn)題:用戶支付完一筆訂單,訂單系統(tǒng)就要同步執(zhí)行一系列接口調(diào)用,導(dǎo)致響應(yīng)慢,用戶體驗(yàn)差;
性能問(wèn)題:瓶頸在數(shù)據(jù)庫(kù),無(wú)法應(yīng)對(duì)“促銷”之類的活動(dòng)帶來(lái)的峰值流量;
耦合問(wèn)題:積分、促銷、通知、物流應(yīng)當(dāng)與核心鏈路解耦;
大數(shù)據(jù)傳輸問(wèn)題:外部系統(tǒng)會(huì)從訂單數(shù)據(jù)庫(kù)里查詢訂單數(shù)據(jù),消耗系統(tǒng)本身的資源;
狀態(tài)補(bǔ)償問(wèn)題:”中間狀態(tài)“的訂單通過(guò)批量掃描的方式進(jìn)行狀態(tài)補(bǔ)償,效率非常低。
本章,我們來(lái)解決同步調(diào)用問(wèn)題、耦合問(wèn)題、性能問(wèn)題。
一、異步
首先,我們分析下整個(gè)業(yè)務(wù)的核心鏈路。用戶支付完成后,最關(guān)鍵的是什么?是更新訂單狀態(tài)以及扣減庫(kù)存,一旦用戶支付成功,只要保證訂單狀態(tài)變?yōu)椤爸Ц冻晒Α?,?kù)存扣減成功,這樣核心數(shù)據(jù)就不會(huì)錯(cuò)亂。而諸如物流發(fā)貨、短信通知、增加積分、促銷優(yōu)惠券之類的操作完全可以異步化:

上圖中,我們引入RocketMQ后,當(dāng)訂單系統(tǒng)更新完訂單狀態(tài)和扣減成功庫(kù)存后,就發(fā)送一條“支付成功”消息到MQ中,然后立即返回。而物流、短信、積分、促銷系統(tǒng)會(huì)訂閱MQ中的該類消息,它們收到通知后就會(huì)去異步處理。
通過(guò)引入MQ,整個(gè)系統(tǒng)的響應(yīng)時(shí)間可以大大縮短。比如,訂單系統(tǒng)本身操作需要耗費(fèi)30ms,調(diào)用庫(kù)存系統(tǒng)接口進(jìn)行庫(kù)存扣減耗費(fèi)80ms,增加積分耗費(fèi)50ms,派發(fā)優(yōu)惠券耗費(fèi)60ms,發(fā)送短信耗費(fèi)100ms,通知發(fā)貨耗費(fèi)500ms。
如果沒(méi)有引入MQ進(jìn)行架構(gòu)改造,每次支付成功后的大量同步接口調(diào)用,耗時(shí)可能接近1秒鐘,而改造后,只需要100ms就可以完成響應(yīng)。
RocketMQ中的Producer支持三種發(fā)送消息的模式:同步、異步回調(diào)、單向異步;Consumer支持兩種消費(fèi)消息的模式:Push模式、Pull模式。具體采用哪種模式,需要結(jié)合具體的業(yè)務(wù)場(chǎng)景去分析,我會(huì)在后面的章節(jié)對(duì)生產(chǎn)者和消費(fèi)者的原理進(jìn)行介紹。
二、解耦
解耦和異步是同生同源的,我們經(jīng)過(guò)上述的異步化改造后,自然而然的就已經(jīng)將物流、短信、積分、促銷系統(tǒng)解耦出去了。
三、削峰
針對(duì)“促銷”場(chǎng)景下的高并發(fā)訪問(wèn)問(wèn)題,瓶頸主要在數(shù)據(jù)庫(kù)。所以,我們必須限制直接打到數(shù)據(jù)庫(kù)上的流量。我們假設(shè)“促銷”是針對(duì)某些商品的秒殺活動(dòng),那么可以從以下幾個(gè)維度進(jìn)行改造:
前端頁(yè)面:設(shè)置校驗(yàn)(驗(yàn)證碼、答題),阻止作弊器刷單;
獨(dú)立出“秒殺系統(tǒng)”,專門負(fù)責(zé)處理“秒殺”活動(dòng)的請(qǐng)求,避免對(duì)正常的訂單系統(tǒng)造成影響;
庫(kù)存預(yù)先寫入Redis,基于Redis做高并發(fā)下的庫(kù)存扣減,一旦庫(kù)存不足,拒絕掉后續(xù)所有秒殺請(qǐng)求;
扣減庫(kù)存成功的秒殺請(qǐng)求,先進(jìn)入MQ,然后由訂單系統(tǒng)訂閱消息并下單。

3.1 前端控制
前端控制其實(shí)很好理解,肯定會(huì)有人在秒殺活動(dòng)開始前不斷的刷商品詳情頁(yè),那么在秒殺活動(dòng)開始的那一瞬間,就會(huì)同時(shí)涌入大量請(qǐng)求。如果采用驗(yàn)證碼或答題方式,就可以將請(qǐng)求錯(cuò)峰,因?yàn)椴煌说拇痤}速度肯定不一樣,這樣就從源頭減少了并發(fā)請(qǐng)求數(shù)。
此外,肯定還會(huì)有人寫作弊腳本進(jìn)行秒殺,答題的方式可以有效屏蔽這種作弊請(qǐng)求。
3.2 獨(dú)立服務(wù)
一般針對(duì)“秒殺”這種促銷活動(dòng),需要獨(dú)立出一套單獨(dú)的服務(wù)進(jìn)行部署,避免對(duì)正常的訂單服務(wù)造成影響。如上圖中,我們對(duì)訂單系統(tǒng)部署了兩個(gè)集群,一個(gè)集群是秒殺系統(tǒng)集群,一個(gè)集群是普通訂單系統(tǒng)集群。即使秒殺系統(tǒng)因業(yè)務(wù)量太大掛了,也不會(huì)普通下單請(qǐng)求出現(xiàn)問(wèn)題。
3.3 庫(kù)存預(yù)熱
由于秒殺商品的數(shù)量是有限制的,且在業(yè)務(wù)上一般會(huì)提前預(yù)告,所以我們可以將秒殺商品的庫(kù)存提前寫到Redis中。因?yàn)槿绻苯幼屆霘⑾到y(tǒng)調(diào)用庫(kù)存系統(tǒng)的接口,進(jìn)行下單,會(huì)對(duì)庫(kù)存系統(tǒng)造成非常大的壓力,而Redis的性能是非常好,可以輕松扛?jìng)€(gè)上萬(wàn)并發(fā)請(qǐng)求。
此外,當(dāng)Redis中的庫(kù)存不足后,可以直接拒絕請(qǐng)求,大幅削減后續(xù)無(wú)效請(qǐng)求對(duì)秒殺系統(tǒng)造成的壓力。
3.4 MQ排隊(duì)
接下來(lái)我們考慮下,假設(shè)1秒鐘內(nèi)秒殺成功了1萬(wàn)件商品,即Redis中庫(kù)存都扣減成功了,那么如果秒殺系統(tǒng)此時(shí)直接去訪問(wèn)數(shù)據(jù)庫(kù)創(chuàng)建訂單,數(shù)據(jù)庫(kù)可能會(huì)直接宕掉。
所以秒殺系統(tǒng)此時(shí)可以發(fā)送一個(gè)“秒殺成功”的消息到MQ中,由普通訂單系統(tǒng)從MQ中消費(fèi)秒殺成功的消息,進(jìn)行常規(guī)性的操作即可。因?yàn)樗查g上萬(wàn)并發(fā)的壓力對(duì)RocketMQ來(lái)說(shuō)可以輕松扛下,普通訂單系統(tǒng)可以根據(jù)自己的工作負(fù)載,慢慢從MQ中消費(fèi)消息,然后操作數(shù)據(jù)庫(kù),這樣就不會(huì)對(duì)數(shù)據(jù)造成很大的壓力。
四、總結(jié)
本章,我們針對(duì)案例系統(tǒng)中存在的部分問(wèn)題進(jìn)行了分析,并通過(guò)RocketMQ對(duì)其進(jìn)行了改造。消息中間件的核心功能就是異步、解耦、削峰。后續(xù)章節(jié),我們會(huì)繼續(xù)解決案例中剩余的幾個(gè)問(wèn)題。
另外,引入MQ后其實(shí)也會(huì)帶來(lái)整個(gè)系統(tǒng)的復(fù)雜度上升,比如消息丟失問(wèn)題、消息有序問(wèn)題、數(shù)據(jù)一致性問(wèn)題等等,后面會(huì)一一介紹,關(guān)注公眾號(hào)Java技術(shù)棧第一時(shí)間推送。
點(diǎn)擊「閱讀原文」獲取面試題大全~
