使用RabbitMQ打造扛得住的高并發(fā)環(huán)境(三)
1. 前言
Hello,大家好。我們?cè)谏弦恍」?jié)中,介紹了使用 RabbitMQ 打造扛得住的高并發(fā)環(huán)境系列內(nèi)容的第二小節(jié)部分,也就是我們此系列小節(jié)內(nèi)容的基礎(chǔ)核心內(nèi)容。
本小節(jié)會(huì)繼續(xù)介紹使用 RabbitMQ 打造扛得住的高并發(fā)環(huán)境系列內(nèi)容的第三部分,在本小節(jié)中,我們會(huì)使用我們?cè)诘诙?jié)中使用 RabbitMQ 和 Redis 打造的高可用消息隊(duì)列去實(shí)現(xiàn)我們的一個(gè)真實(shí)的高并發(fā)的業(yè)務(wù)場(chǎng)景。
本小節(jié)會(huì)首先介紹一種老師在自己的實(shí)際工作中,出現(xiàn)的真實(shí)的高并發(fā)業(yè)務(wù)場(chǎng)景,并且,老師在應(yīng)對(duì)這種高并發(fā)業(yè)務(wù)場(chǎng)景時(shí),采用的基本解決方案就是采用的 RabbitMQ 和 Redis 共同改造的高可用消息隊(duì)列這種形式,還在等什么,讓我們先睹為快吧!
本節(jié)主要內(nèi)容:
-
實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景概述;
-
實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景實(shí)現(xiàn);
-
實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景測(cè)試。
2.實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景概述
本部分內(nèi)容,老師會(huì)詳細(xì)介紹本套課程最終需要實(shí)現(xiàn)的一種業(yè)務(wù)場(chǎng)景,此種業(yè)務(wù)場(chǎng)景是老師自己在實(shí)際工作中遇到的真實(shí)的業(yè)務(wù)場(chǎng)景,同學(xué)們一定要先對(duì)這種業(yè)務(wù)場(chǎng)景有個(gè)清晰地了解之后,在繼續(xù)學(xué)習(xí)本小節(jié)后續(xù)地內(nèi)容,如果你對(duì)這種業(yè)務(wù)場(chǎng)景沒有充分地了解,那么后面的實(shí)現(xiàn)思路你將不會(huì)看懂,這點(diǎn)同學(xué)們注意。
本業(yè)務(wù)場(chǎng)景實(shí)際上并不算復(fù)雜,我們每個(gè)同學(xué)在真實(shí)的日常生活中,或多或少都會(huì)接觸到,只不過平時(shí)同學(xué)們可能不會(huì)注意觀察或者思考。這種業(yè)務(wù)場(chǎng)景有一個(gè)專有的代名詞,相信大家都已經(jīng)聽說過了,那就是’秒殺’業(yè)務(wù)場(chǎng)景。
那么,什么是秒殺業(yè)務(wù)場(chǎng)景呢?
這個(gè)秒殺的業(yè)務(wù)場(chǎng)景,出現(xiàn)在銷售行業(yè)的居多,比如日常生活中,我們?cè)诔兄腥ベ徺I一種商品,這種商品的價(jià)格要比往常的價(jià)格要低很多,但是,這種商品的庫存數(shù)量是有限的,當(dāng)我們購買這種商品時(shí),必須要在一瞬間完成搶購這一動(dòng)作。
隨著互聯(lián)網(wǎng)時(shí)代的快速發(fā)展,越來越多的線上電子商城已經(jīng)出現(xiàn)在人們的日常生活中,以淘寶、京東為代表性的互聯(lián)網(wǎng)電子商城巨頭率先將這些線下的商品購買行為,轉(zhuǎn)換為線上的商品購買功能。
針對(duì)與上述這種秒殺搶購的業(yè)務(wù)場(chǎng)景,目前在各互聯(lián)網(wǎng)電子商城巨頭中都是有所體現(xiàn)的,比如我們熟知的雙十一活動(dòng),以及 618 商品大促活動(dòng),這些都是秒殺搶購業(yè)務(wù)場(chǎng)景的典型代表,那么,這種業(yè)務(wù)場(chǎng)景在線上又是如何實(shí)現(xiàn)的呢?
在分析一秒殺搶購業(yè)務(wù)場(chǎng)景的一個(gè)完整的線上業(yè)務(wù)流程是什么樣的之前,我們先來看一下,一般地線上商品購買的一個(gè)完整的業(yè)務(wù)流程是什么樣的,如下圖所示:

首先,用戶在有這種秒殺搶購的實(shí)際需求之后,用戶首先會(huì)登錄我們的線上商城系統(tǒng),在用戶成功登錄本系統(tǒng)之后,用戶需要到我們線上商城系統(tǒng)的秒殺搶購專區(qū),用戶可以在這個(gè)秒殺搶購專區(qū)中看到本商城系統(tǒng)中參與秒殺搶購活動(dòng)的商品,這一過程我們稱為用戶挑選商品階段。
用戶在挑選完自己所需要的商品之后,可以將所需的商品放入購物車中,也可以直接點(diǎn)擊下單按鈕,來迅速完成對(duì)某一具體商品的下單操作。
如果用戶是將商品放入購物車中,那么用戶只能進(jìn)行一個(gè)批量下單的動(dòng)作,即用戶前往自己的購物車中,選中商品之后,點(diǎn)擊下單按鈕,進(jìn)行一個(gè)批量下單操作,這一過程我們稱為用戶預(yù)下單階段。
在用戶將訂單創(chuàng)建完畢之后,就需要用戶選擇對(duì)應(yīng)的支付方式,來完成商品價(jià)格的支付動(dòng)作,對(duì)于線上電子商城而言,用戶可以選擇不同廠家的掃碼支付功能來完成支付,這一過程我們稱為用戶支付階段。
在用戶對(duì)所購商品支付完成之后,我們需要將用戶的商品支付結(jié)果返回給用戶,告知用戶商品支付的狀態(tài),是支付成功了,還是支付過程中遇到問題,導(dǎo)致支付失敗了,這一過程往往我們會(huì)采取輪詢的方式實(shí)現(xiàn),這一過程我們稱為用戶支付狀態(tài)回調(diào)階段。
在用戶的支付狀態(tài)成功回調(diào)給用戶之后,一個(gè)完整的線上商品購買流程就結(jié)束了,至于后續(xù)地商品物流信息等其他信息就不屬于我們商品購買的流程了。
在清楚了一般地商品購買全流程之后,我們就不難理解秒殺搶購的業(yè)務(wù)場(chǎng)景流程了。
其實(shí),秒殺搶購的業(yè)務(wù)場(chǎng)景流程和一般地商品購買流程是一模一樣地,只不過在用戶預(yù)下單階段,以及用戶支付階段,在同一時(shí)刻會(huì)有大量的用戶請(qǐng)求需要我們處理,這就是秒殺業(yè)務(wù)場(chǎng)景和一般地商品購買流程中最大的區(qū)別點(diǎn),其他地方并沒有什么區(qū)別。
在本小節(jié)中,我們需要實(shí)現(xiàn)上述業(yè)務(wù)場(chǎng)景,并對(duì)核心的秒殺搶購業(yè)務(wù)場(chǎng)景中的用戶預(yù)下單階段,以及用戶支付階段,做好高并發(fā)場(chǎng)景下的處理。
Tips: 同學(xué)們一定要清楚地理解上述所介紹的業(yè)務(wù)流程,如果看一遍不理解,那就反復(fù)多看幾遍,直到自己理解了即可。
3.實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景實(shí)現(xiàn)
在了解了秒殺搶購的業(yè)務(wù)場(chǎng)景流程之后,接下來我們就需要實(shí)現(xiàn)這一業(yè)務(wù)場(chǎng)景了,那么,這種業(yè)務(wù)場(chǎng)景我們應(yīng)該怎么用 RabbitMQ 和 Redis 去實(shí)現(xiàn)呢?
在使用 RabbitMQ 打造扛得住的高并發(fā)環(huán)境系列小節(jié)內(nèi)容的第二小節(jié)中,我們使用 RabbitMQ 消息通信中間件和 Redis 緩存中間件,對(duì) RabbitMQ 自身的消息隊(duì)列進(jìn)行了改造,改造成了一種 Redis 承載的高可用的消息隊(duì)列,在本節(jié),我們就會(huì)用到這一高可用的消息隊(duì)列。
在實(shí)現(xiàn)上述實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景時(shí),由于篇幅原因,我們并不會(huì)從用戶登錄開始,逐步地去實(shí)現(xiàn)每一個(gè)過程,我們只實(shí)現(xiàn)在秒殺搶購業(yè)務(wù)場(chǎng)景中,最核心的部分,也就是,當(dāng)我們?cè)诿霘屬徤唐穮^(qū)域,點(diǎn)擊立即購買這個(gè)秒殺按鈕時(shí),我們后臺(tái)所需要應(yīng)對(duì)高并發(fā)處理的內(nèi)容。
讓我們來看看具體應(yīng)該怎么設(shè)計(jì)實(shí)現(xiàn)吧。
3.1 初始化 Redis 緩存數(shù)據(jù)
當(dāng)我們點(diǎn)擊立即購買這個(gè)秒殺按鈕時(shí),我們首先會(huì)獲取到用戶所挑選的商品數(shù)據(jù), 在獲取到這些商品數(shù)據(jù)之后,我們需要根據(jù)這些商品數(shù)據(jù)中起到唯一區(qū)分商品的這一屬性,去查詢我們對(duì)應(yīng)商品的庫存是否充足。
查詢庫存這種操作,我們放在 Redis 緩存中進(jìn)行存儲(chǔ)。即,當(dāng)我們的后臺(tái)服務(wù)啟動(dòng)時(shí),或者是在一個(gè)其他的什么時(shí)機(jī)的時(shí)候,我們會(huì)將系統(tǒng)中參與秒殺搶購的所有商品數(shù)據(jù),或者這些關(guān)鍵的商品數(shù)據(jù),放入到我們的 Redis 緩存中,這些數(shù)據(jù)中間就包括商品的庫存數(shù)量,如下代碼所示:
代碼實(shí)現(xiàn):
// 向 Redis 緩存中初始化存儲(chǔ)秒殺商品數(shù)據(jù)
redisUtil.set("shipping_seckill" + shipping.getId(), shipping);
代碼解釋:
我們使用 redisUtil 工具類的 set 方法,將參與秒殺搶購的商品數(shù)據(jù) shipping ,在后臺(tái)服務(wù)初始化時(shí),放入到 Redis 緩存中,以備后續(xù)使用。
3.2 Redis 預(yù)減商品庫存
在校驗(yàn)商品庫存時(shí),我們會(huì)只查詢 Redis 緩存中的商品數(shù)據(jù), 查看商品庫存是否足夠支撐用戶的商品購買數(shù)量,如果庫存數(shù)量不足以支撐,則提示用戶商品庫存不足,預(yù)下單失敗;如果庫存數(shù)量充足,則提示用戶商品預(yù)下單成功,如下代碼所示:
代碼實(shí)現(xiàn):
Shipping shipping = redisUtil.get("shipping_seckill" + shipping.getId());
Integer shippingStorage = shipping.getStorage();
if(shippingStorage >= userCurrNums) {
// 預(yù)減庫存,并提示用戶商品預(yù)下單成功
}
代碼解釋:
第 1 行,我們使用 redisUtil 工具類的 get 方法,來獲取到存儲(chǔ)于 Redis 緩存中的商品數(shù)據(jù)。
第 2 行,我們通過聲明一個(gè) shippingStorage 變量,來進(jìn)一步獲取到用戶所挑選商品的庫存信息。
第 3-5 行,我們通過將商品庫存 shippingStorage 與用戶所購買的商品數(shù)量 userCurrNums 做一個(gè)比較,來判斷當(dāng)前商品的庫存是否充足,并將結(jié)果提示給用戶。
3.3 RabbitMQ 處理秒殺商品訂單
在商品預(yù)減庫存成功之后,我們需要根據(jù)實(shí)際的業(yè)務(wù)需求來生成商品的訂單,并將該訂單發(fā)送到我們的 RabbitMQ 消息隊(duì)列中去,如下代碼所示:
代碼實(shí)現(xiàn):
Shipping shipping = redisUtil.get("shipping_seckill" + shipping.getId());
Integer shippingStorage = shipping.getStorage();
if(shippingStorage >= userCurrNums) {
// 預(yù)減庫存成功,開始處理商品訂單
rabbitTemplate.convertAndSend("seckill_order_ex", "seckill_order_key", order, message -> {
// 設(shè)置具體的消費(fèi)配置參數(shù)
})
}
代碼解釋:
第 1-3 行,和上述代碼相同,不再贅述。
第 5 行,我們使用 rabbitTemplate 的 convertAndSend 方法,來設(shè)置秒殺商品訂單所需要的交換機(jī),routing key ,并將秒殺商品訂單發(fā)送到我們的 RabbitMQ 中。
將秒殺商品訂單發(fā)送到我們的 RabbitMQ 中之后,我們還需要對(duì)消息配置一種監(jiān)聽器,來監(jiān)聽消息有沒有真正到達(dá) RabbitMQ 的交換機(jī)中。(由于篇幅原因,配置監(jiān)聽部分請(qǐng)參考《RabbitMQ消息確認(rèn)與返回機(jī)制介紹》小節(jié))。
3.4 消費(fèi)者完成商品支付
配置好監(jiān)聽之后,我們的消費(fèi)者就需要將消息進(jìn)行消費(fèi)了。從 RabbitMQ 消息隊(duì)列中獲取到消息之后,會(huì)返回給我們一個(gè)消息 ack 或 nack 的應(yīng)答結(jié)果,表明消費(fèi)者是否成功獲取到了消息。
當(dāng)返回 ack 確認(rèn)應(yīng)答結(jié)果時(shí),表明消費(fèi)者已經(jīng)成功獲取到了消息,此時(shí),我們應(yīng)該根據(jù)消費(fèi)者所獲取到的秒殺訂單數(shù)據(jù),來生成對(duì)應(yīng)場(chǎng)景的支付二維碼,以提示用戶掃碼來完成商品支付。
當(dāng)用戶支付成功之后,我們需要根據(jù)用戶的支付結(jié)果,同步更新我們 Redis 緩存,以及數(shù)據(jù)庫中,用戶秒殺商品和秒殺訂單的商品庫存數(shù)據(jù),以及訂單的狀態(tài), 實(shí)現(xiàn)代碼如下所示:
代碼實(shí)現(xiàn):
switch (order.getPayState()) {
case 0 :
this.updateSeckillOrderInfo(order.getShippingId(), 0);
// 下同
default:
break;
}
代碼解釋:
第 1 行,我們使用了 switch 語句,來獲取用戶秒殺訂單的支付狀態(tài)。
第 2-7 行,我們使用 case 語句,來對(duì)不同的用戶秒殺訂單的支付狀態(tài)做出不同的數(shù)據(jù)處理,比如,支付狀態(tài)為 0 時(shí),表示用戶支付成功,那么此時(shí)我就需要調(diào)用 updateSeckillOrderInfo 方法來更新用戶秒殺訂單的支付狀態(tài)。
至此,我們已經(jīng)基本實(shí)現(xiàn)了秒殺搶購業(yè)務(wù)場(chǎng)景中的核心內(nèi)容,即用戶點(diǎn)擊立即購買之后,我們的后臺(tái)服務(wù)來處理高并發(fā)請(qǐng)求的業(yè)務(wù)場(chǎng)景。
4.實(shí)際高并發(fā)業(yè)務(wù)場(chǎng)景測(cè)試
那么,我們雖然基本實(shí)現(xiàn)了這一業(yè)務(wù)場(chǎng)景中的核心部分內(nèi)容,但是,我們的這個(gè)秒殺搶購商品的接口可以同時(shí)支撐多大的 QPS 呢?
接下來,讓我們使用 JMeter 來做一個(gè)簡(jiǎn)單的測(cè)試,來看一下我們采用 RabbitMQ 和 Redis 的方式之下,我們這個(gè)服務(wù)接口的 QPS 吞吐量是多少,測(cè)試結(jié)果如下圖所示:

Tips:
1. 關(guān)于 JMeter 我們不會(huì)做任何介紹,需要同同學(xué)們課下進(jìn)行了解。
2. 由于測(cè)試環(huán)境的不同,使用 JMter 測(cè)試的結(jié)果也會(huì)不進(jìn)相同,上下存在一定的誤差,這是合理的。
3. 關(guān)于 QPS 的概念介紹,也需要同學(xué)們自行了解,本套課程不會(huì)做任何介紹。
4. 測(cè)試部分并不是我們課程內(nèi)容的重點(diǎn),只不過是為了體現(xiàn)一種量化的指標(biāo),來供同學(xué)們查看,這點(diǎn)同學(xué)們要搞清楚。
5. 小結(jié)

本小節(jié)為同學(xué)們介紹了使用 RabbitMQ 打造扛得住的高并發(fā)環(huán)境的第三部分內(nèi)容,其主要介紹了,如何使用 RabbitMQ 和 Redis 共同打造的高可用消息隊(duì)列,來實(shí)現(xiàn)我們真實(shí)的一種秒殺搶購的業(yè)務(wù)場(chǎng)景,希望同學(xué)們都能夠理解這部分內(nèi)容。
隨著使用 RabbitMQ 打造扛得住的高并發(fā)環(huán)境系列小節(jié)內(nèi)容的結(jié)束,本套課程的進(jìn)度也接近了尾聲。通過對(duì) RabbitMQ 消息通信中間件系統(tǒng)性地介紹,希望同學(xué)們可以將 RabbitMQ 真正地應(yīng)用到我們的項(xiàng)目中去。
最后,感謝各位同學(xué)的持續(xù)關(guān)注,本套課程就到這里了,希望同學(xué)們可以持續(xù)支持與關(guān)注,江湖路遠(yuǎn),我們有緣再會(huì)!