為什么要做秒殺?這個不難解釋,最起碼對于互聯(lián)網(wǎng)電商業(yè)務(wù)來說很常見,那怎么樣才能設(shè)計(jì)出相對比較完善的秒殺策略呢?我覺得這其中有兩個關(guān)鍵點(diǎn):
談到秒殺,我們的第一反應(yīng)就是多人次搶一款或者幾款產(chǎn)品導(dǎo)致瞬間產(chǎn)生的流量峰值很大,那如何支持高并發(fā)就是其中的重點(diǎn)之一。
庫存怎么來鎖,每件商品有限定的秒殺庫存數(shù),我們怎么來變更庫存信息,MySQL數(shù)據(jù)庫直接操作么,又或者是通過Java中的原子性類來維護(hù)庫存信息?
下面我來談?wù)勎覀兊膶?shí)踐。
整體系統(tǒng)架構(gòu)
Web層,主要是APP、HTML 5、PC端的用戶流量,目前是7:1:2 流量占比。
反爬和網(wǎng)關(guān)是公司層面的用于反爬蟲以及網(wǎng)關(guān)路由。
Restful API主要是用于收集搜索、產(chǎn)品詳情、秒殺信息,用于后端跟前端模板的一個映射。
SecKill API主要是用于訪問產(chǎn)品的庫存信息以及產(chǎn)品的秒殺信息,獨(dú)立出秒殺API對原有的產(chǎn)品API下單服務(wù)不影響,減少原有系統(tǒng)的耦合。
Booking和Order API我就不分開來講,當(dāng)秒殺成功以后用于生成訂單。
大膽設(shè)計(jì)
關(guān)于怎么支持高并發(fā)有兩種策略可以結(jié)合,通過前端限流機(jī)制只放10%左右的流量到后端,90%的人直接提示秒殺結(jié)束,下面我主要是講講后端怎么實(shí)現(xiàn)。我想Redis大家都用過,其高并發(fā)能力超強(qiáng),理論峰值是單機(jī)每秒能支持10萬次讀寫,Redis還可以支持分布式集群擴(kuò)展性強(qiáng)。還有一點(diǎn)Redis更新操作是原子性的,更新數(shù)據(jù)是單線程的安全有保證。鎖定庫存就用Redis來實(shí)現(xiàn),大致的流程如下:
流程梳理
預(yù)熱緩存,即將產(chǎn)品信息以及庫存信息刷新到緩存之中,難道只存這些信息么?這兩項(xiàng)是最主要的,其它附加的后面會講到。
后臺應(yīng)用接收前端的訪問,我為什么要明確畫出Tomcat容器,這個后續(xù)也是也有用的。
通過產(chǎn)品信息為key,去對Redis庫存信息執(zhí)行庫存減操作。
有個long型值返回。
判斷返回值是否大于或者等于0。
執(zhí)行到這里說明該用戶應(yīng)該是可以下訂單購買的。
直接操作insert DB?沒錯像訂單這么重要的信息,還是應(yīng)該落庫為安。
秒殺商品庫存不足,直接返回秒殺結(jié)束。
缺陷思考
在緩存預(yù)熱好,秒殺開始之前有流量進(jìn)來怎么辦?或者秒殺已經(jīng)庫存為0應(yīng)該結(jié)束秒殺怎么辦?為了解決這個問題我們應(yīng)該在步驟1的時(shí)候添加秒殺開始和秒殺結(jié)束時(shí)間,作為操作8開始之前的第一道判斷請求。如果在開始之前以及在結(jié)束時(shí)間之后則直接返回秒殺未開始或者秒殺已經(jīng)結(jié)束。所以更為完善的流程圖應(yīng)該如下:
操作11中,得知有庫存直接下訂單會不會真對數(shù)據(jù)庫造成很大壓力呢?肯定會的,所以先期可以減少秒殺的產(chǎn)品數(shù)以及庫存數(shù)量,如果調(diào)用訂單接口插入訂單數(shù)據(jù)失敗,應(yīng)該釋放庫存信息,讓請求重新可以秒殺產(chǎn)品信息。
如何評估應(yīng)該部署多少臺應(yīng)用服務(wù)器和Redis集群?單臺Tomcat容器的每秒訪問上限是1000左右,單臺Redis機(jī)器低估一下每秒也可以抗1萬次讀寫。舉例來說,如果秒殺的每秒的訪問是1萬,部署10臺應(yīng)用服務(wù)器,一個Redis集群,2-3臺Redis服務(wù)器(可寫的redis節(jié)點(diǎn))理論就都可以了(這邊峰值計(jì)算容器級別的Redis級別需要分開來算,因?yàn)橄刃枰L問Tomcat應(yīng)用再去訪問Redis,Tomcat也有連接數(shù)上限)。當(dāng)然一個整個流程下來其實(shí)是不需要1秒的,大致100毫秒左右,所以理論抗并發(fā)峰值應(yīng)該是可以抗1萬 * 10左右。