秒殺項(xiàng)目異步下單的流程一:
瀏覽器發(fā)送請(qǐng)求,首先請(qǐng)求到服務(wù)時(shí),會(huì)先執(zhí)行參數(shù)校驗(yàn),如果校驗(yàn)失敗直接返回給前端,如果校驗(yàn)成功則執(zhí)行下單方法
? 其中的校驗(yàn)方法主要是利用redis的緩存機(jī)制,即所有參加校驗(yàn)的數(shù)據(jù)都存入redis中,因?yàn)檫@些是熱點(diǎn)數(shù)據(jù),需要頻繁訪問,利用vo的校驗(yàn),和庫存的校驗(yàn)就需要存入redis中,首先就是redis要在項(xiàng)目開始的時(shí)候進(jìn)行初始化,為了是將所有的用戶存入redis中,這樣不用頻繁訪問數(shù)據(jù)庫獲取用戶信息,還有將將獲取所有的秒殺商品對(duì)象即voList結(jié)合,然后遍歷voList集合,將每個(gè)vo轉(zhuǎn)為json字符串存入redis中(需要定義redis的key,這里的redis使用hash的數(shù)據(jù)結(jié)構(gòu)進(jìn)行存儲(chǔ),避免key值太多,造成hash沖突),然后獲取每個(gè)vo對(duì)象中的庫存信息,然后同樣定義一個(gè)key存入redis中,(由于使用的是hash結(jié)構(gòu)存儲(chǔ),那么最外層的key就是一個(gè)標(biāo)識(shí),表示存的是庫存還是秒殺商品,里層的key用秒殺商品id存儲(chǔ),value就是存vo對(duì)象或庫存),
? 這樣初始化redis之后,所有的熱點(diǎn)數(shù)據(jù)都存入redis中了,校驗(yàn)時(shí),比如校驗(yàn)秒殺商品是否存在,需要根據(jù)頁面?zhèn)鞯拿霘⑸唐穒d去redis中查詢有沒有對(duì)應(yīng)的商品信息,如果有,需要判斷這個(gè)商品是否在活動(dòng)范圍之內(nèi)(用時(shí)間進(jìn)行判斷),需要判斷用戶是否已經(jīng)下過單(使用redis的setnx指令對(duì)應(yīng)的代碼,表示沒有就存,有就不存),最后判斷庫存是否足夠(使用redis去緩存中查使用increment機(jī)制,進(jìn)行原子性遞增(-1)),當(dāng)校驗(yàn)成功后,需要下單,這時(shí)候就要利用rocketMQ發(fā)送異步下單,到消息中間件上,無論后面的消費(fèi)者執(zhí)行下單成功與否,消息中間件都會(huì)響應(yīng)給瀏覽器,可以響應(yīng)正在排隊(duì)中,或其他,,,,
當(dāng)將消息發(fā)送到消息中間件上以后,會(huì)與namesever建立長(zhǎng)連接,然后將消息存入到中間件的bucket中,bucket中有一個(gè)topic專門存入一些主題消息,一個(gè)topic中可以存入4個(gè)sequence消息隊(duì)列,然后消費(fèi)者也就是需要下單的業(yè)務(wù)會(huì)訂閱這個(gè)消息中間件,消費(fèi)者其實(shí)就是一個(gè)監(jiān)聽器,用來監(jiān)聽消息中間件上的消息,然后從上面獲取消息,獲取指定tag的消息,也就是一個(gè)對(duì)象(這個(gè)對(duì)象封裝了下單需要的秒殺商品id和用戶id),然后消費(fèi)者獲取這兩個(gè)id,然后調(diào)用下單的方法,將這兩個(gè)參數(shù)傳進(jìn)入然后獲取下單id,但是并不能保證下單一定成功,需要進(jìn)行try-catch
? 然后在try里面要發(fā)送下單成功的消息到消息中間件中(目的是需要告訴瀏覽器剛才的請(qǐng)求是否發(fā)送成功),將獲取的下單id和uuid(uuid是從前端頁面?zhèn)鬟^來的id)封裝成一個(gè)對(duì)象作為發(fā)送rocketMQ異步請(qǐng)求的消息發(fā)送至消息中心
? 消息中心獲取到這個(gè)消息以后會(huì)存入到bucket中.這時(shí)候?yàn)榱俗尀g覽器可以隨時(shí)獲取發(fā)送請(qǐng)求之后響應(yīng)這里使用一個(gè)與瀏覽器進(jìn)行長(zhǎng)連接的一個(gè)協(xié)議即為:WebSocket協(xié)議,因此需要新建一個(gè)websocket服務(wù)作為與瀏覽器進(jìn)行長(zhǎng)連接的一個(gè)服務(wù),(前端會(huì)發(fā)送一個(gè)請(qǐng)求與WebSocket建立長(zhǎng)連接)它既是消費(fèi)者也是生產(chǎn)者,它會(huì)作為消費(fèi)者從消息中心上將秒殺成功或者失敗的消息獲取下來,然后獲取消息中的參數(shù):uuid(非常重要),下單id 然后通過webSocketServer的clients將uuid作為參數(shù)獲取指定的客戶端,也就是所謂的session.如果session不為空,則創(chuàng)建一個(gè)result對(duì)象,將下單成功的消息對(duì)象封裝進(jìn)入result對(duì)象中,然后通過session.getBasicRemote().sentText()方法將result對(duì)象傳進(jìn)去,即可以給瀏覽器發(fā)送請(qǐng)求,且這個(gè)連接是長(zhǎng)連接,主要是通過uuid獲取哪個(gè)用戶的發(fā)送的請(qǐng)求,然后通過WebSocket找到這用戶然后將相應(yīng)給他,這就是mq異步下單的大致流程.....
RocketMQ異步下單流程二:
主要是針對(duì)創(chuàng)建訂單失敗和成功的情況,當(dāng)秒殺服務(wù)中創(chuàng)建訂單的監(jiān)聽器在接收到注冊(cè)中心中的消息以后,需要去執(zhí)行創(chuàng)建訂單的業(yè)務(wù),(扣減庫存,創(chuàng)建基礎(chǔ)訂單 和秒殺訂單)同時(shí)也會(huì)發(fā)送消息到注冊(cè)中心,告知消息中心秒殺成功,如果創(chuàng)建訂單失敗,還用還要在catch中往消息中心發(fā)送秒殺失敗的消息,(同時(shí)需要redis庫存回補(bǔ),清除用戶下單標(biāo)記和本地標(biāo)記)
? 如果秒殺成功,即下單成功,還需要在發(fā)送一個(gè)延遲消息到消息中心(用來檢查訂單是夠超時(shí)未支付)
? 秒殺服務(wù)中新創(chuàng)建一個(gè)訂單成功后延遲消息的監(jiān)聽器(主要用來監(jiān)聽訂單是否超時(shí)未支付),如果監(jiān)聽到消息,則表示訂單需要支付的時(shí)間已過期,這時(shí),需要判斷訂單是否已支付(通過查詢訂單),如果已支付則不用做任何操作,如果未支付,需要將訂單的支付狀態(tài)改為超時(shí)未支付狀態(tài),同時(shí)還需要進(jìn)行mysql數(shù)據(jù)庫中的庫存回補(bǔ),然后將庫存同步到redis庫存中,還用清除本地標(biāo)記(比如:當(dāng)前庫存為0時(shí),由于可能秒殺服務(wù)器有多個(gè),只清除本次的這臺(tái)服務(wù)器有可能造成庫存回補(bǔ)了,其他用戶在其他服務(wù)器上進(jìn)行下單時(shí)會(huì)顯示庫存不足,這就是由于本地標(biāo)識(shí)存在,沒有清除,導(dǎo)致庫存不足)因此為了能夠清除所有服務(wù)器的本地標(biāo)識(shí),使用消息中間件的廣播消息這個(gè)機(jī)制去發(fā)送消息,這樣所有的服務(wù)器都會(huì)收到清除本地標(biāo)識(shí)的消息,這樣創(chuàng)建一個(gè)清除本地 標(biāo)識(shí)的監(jiān)聽器進(jìn)行監(jiān)聽,當(dāng)收到消息時(shí),則調(diào)用controller去清除本地標(biāo)識(shí)(將map緩存中的true改為false)
當(dāng)秒殺服務(wù)創(chuàng)建訂單失敗后,有創(chuàng)建訂單的監(jiān)聽器根據(jù)創(chuàng)建訂單失敗的異常信息作為消息發(fā)送到消息中心中,這時(shí)候需要在webSocket服務(wù)中創(chuàng)建一個(gè)秒殺失敗的監(jiān)聽器,專門用來監(jiān)聽失敗消息,如果監(jiān)聽到消息,則會(huì)將消息中的異常信息封裝進(jìn)result對(duì)象中,然后根據(jù)uuid找到對(duì)應(yīng)的客戶端,然后通過session.getBassicRemote.sentText()將result傳到前端頁面,用戶就可以知道下單失敗了