【高并發(fā)】高并發(fā)秒殺系統(tǒng)架構解密,不是所有的秒殺都是秒殺!
來自:冰河技術
前言
很多小伙伴反饋說,高并發(fā)專題學了那么久,但是,在真正做項目時,仍然不知道如何下手處理高并發(fā)業(yè)務場景!甚至很多小伙伴仍然停留在只是簡單的提供接口(CRUD)階段,不知道學習的并發(fā)知識如何運用到實際項目中,就更別提如何構建高并發(fā)系統(tǒng)了!
究竟什么樣的系統(tǒng)算是高并發(fā)系統(tǒng)?今天,我們就一起解密高并發(fā)業(yè)務場景下典型的秒殺系統(tǒng)的架構,結合高并發(fā)專題下的其他文章,學以致用。
電商系統(tǒng)架構
在電商領域,存在著典型的秒殺業(yè)務場景,那何謂秒殺場景呢。簡單的來說就是一件商品的購買人數(shù)遠遠大于這件商品的庫存,而且這件商品在很短的時間內就會被搶購一空。比如每年的618、雙11大促,小米新品促銷等業(yè)務場景,就是典型的秒殺業(yè)務場景。
我們可以將電商系統(tǒng)的架構簡化成下圖所示。
由圖所示,我們可以簡單的將電商系統(tǒng)的核心層分為:負載均衡層、應用層和持久層。接下來,我們就預估下每一層的并發(fā)量。
假如負載均衡層使用的是高性能的Nginx,則我們可以預估Nginx最大的并發(fā)度為:10W+,這里是以萬為單位。
假設應用層我們使用的是Tomcat,而Tomcat的最大并發(fā)度可以預估為800左右,這里是以百為單位。
假設持久層的緩存使用的是Redis,數(shù)據庫使用的是MySQL,MySQL的最大并發(fā)度可以預估為1000左右,以千為單位。Redis的最大并發(fā)度可以預估為5W左右,以萬為單位。
所以,負載均衡層、應用層和持久層各自的并發(fā)度是不同的,那么,為了提升系統(tǒng)的總體并發(fā)度和緩存,我們通??梢圆扇∧男┓桨改兀?/p>
(1)系統(tǒng)擴容
系統(tǒng)擴容包括垂直擴容和水平擴容,增加設備和機器配置,絕大多數(shù)的場景有效。
(2)緩存
本地緩存或者集中式緩存,減少網絡IO,基于內存讀取數(shù)據。大部分場景有效。
(3)讀寫分離
采用讀寫分離,分而治之,增加機器的并行處理能力。
秒殺系統(tǒng)的特點
對于秒殺系統(tǒng)來說,我們可以從業(yè)務和技術兩個角度來闡述其自身存在的一些特點。
秒殺系統(tǒng)的業(yè)務特點
這里,我們可以使用12306網站來舉例,每年春運時,12306網站的訪問量是非常大的,但是網站平時的訪問量卻是比較平緩的,也就是說,每年春運時節(jié),12306網站的訪問量會出現(xiàn)瞬時突增的現(xiàn)象。
再比如,小米秒殺系統(tǒng),在上午10點開售商品,10點前的訪問量比較平緩,10點時同樣會出現(xiàn)并發(fā)量瞬時突增的現(xiàn)象。
所以,秒殺系統(tǒng)的流量和并發(fā)量我們可以使用下圖來表示。
由圖可以看出,秒殺系統(tǒng)的并發(fā)量存在瞬時凸峰的特點,也叫做流量突刺現(xiàn)象。
我們可以將秒殺系統(tǒng)的特點總結如下。
(1)限時、限量、限價
在規(guī)定的時間內進行;秒殺活動中商品的數(shù)量有限;商品的價格會遠遠低于原來的價格,也就是說,在秒殺活動中,商品會以遠遠低于原來的價格出售。
例如,秒殺活動的時間僅限于某天上午10點到10點半,商品數(shù)量只有10萬件,售完為止,而且商品的價格非常低,例如:1元購等業(yè)務場景。
限時、限量和限價可以單獨存在,也可以組合存在。
(2)活動預熱
需要提前配置活動;活動還未開始時,用戶可以查看活動的相關信息;秒殺活動開始前,對活動進行大力宣傳。
(3)持續(xù)時間短
購買的人數(shù)數(shù)量龐大;商品會迅速售完。
在系統(tǒng)流量呈現(xiàn)上,就會出現(xiàn)一個突刺現(xiàn)象,此時的并發(fā)訪問量是非常高的,大部分秒殺場景下,商品會在極短的時間內售完。
秒殺系統(tǒng)的技術特點
我們可以將秒殺系統(tǒng)的技術特點總結如下。
(1)瞬時并發(fā)量非常高
大量用戶會在同一時間搶購商品;瞬間并發(fā)峰值非常高。
(2)讀多寫少
系統(tǒng)中商品頁的訪問量巨大;商品的可購買數(shù)量非常少;庫存的查詢訪問數(shù)量遠遠大于商品的購買數(shù)量。
在商品頁中往往會加入一些限流措施,例如早期的秒殺系統(tǒng)商品頁會加入驗證碼來平滑前端對系統(tǒng)的訪問流量,近期的秒殺系統(tǒng)商品詳情頁會在用戶打開頁面時,提示用戶登錄系統(tǒng)。這都是對系統(tǒng)的訪問進行限流的一些措施。
(3)流程簡單
秒殺系統(tǒng)的業(yè)務流程一般比較簡單;總體上來說,秒殺系統(tǒng)的業(yè)務流程可以概括為:下單減庫存。
針對這種短時間內大流量的系統(tǒng)來說,就不太適合使用系統(tǒng)擴容了,因為即使系統(tǒng)擴容了,也就是在很短的時間內會使用到擴容后的系統(tǒng),大部分時間內,系統(tǒng)無需擴容即可正常訪問。那么,我們可以采取哪些方案來提升系統(tǒng)的秒殺性能呢?
秒殺系統(tǒng)方案
針對秒殺系統(tǒng)的特點,我們可以采取如下的措施來提升系統(tǒng)的性能。
(1)異步解耦
將整體流程進行拆解,核心流程通過隊列方式進行控制。
(2)限流防刷
控制網站整體流量,提高請求的門檻,避免系統(tǒng)資源耗盡。
(3)資源控制
將整體流程中的資源調度進行控制,揚長避短。
由于應用層能夠承載的并發(fā)量比緩存的并發(fā)量少很多。所以,在高并發(fā)系統(tǒng)中,我們可以直接使用OpenResty由負載均衡層訪問緩存,避免了調用應用層的性能損耗。大家可以到https://openresty.org/cn/來了解有關OpenResty更多的知識。同時,由于秒殺系統(tǒng)中,商品數(shù)量比較少,我們也可以使用動態(tài)渲染技術,CDN技術來加速網站的訪問性能。
如果在秒殺活動開始時,并發(fā)量太高時,我們可以將用戶的請求放入隊列中進行處理,并為用戶彈出排隊頁面。
秒殺系統(tǒng)時序圖
網上很多的秒殺系統(tǒng)和對秒殺系統(tǒng)的解決方案,并不是真正的秒殺系統(tǒng),他們采用的只是同步處理請求的方案,一旦并發(fā)量真的上來了,他們所謂的秒殺系統(tǒng)的性能會急劇下降。我們先來看一下秒殺系統(tǒng)在同步下單時的時序圖。
同步下單流程
1.用戶發(fā)起秒殺請求
在同步下單流程中,首先,用戶發(fā)起秒殺請求。商城服務需要依次執(zhí)行如下流程來處理秒殺請求的業(yè)務。
(1)識別驗證碼是否正確
商城服務判斷用戶發(fā)起秒殺請求時提交的驗證碼是否正確。
(2)判斷活動是否已經結束
驗證當前秒殺活動是否已經結束。
(3)驗證訪問請求是否處于黑名單
在電商領域中,存在著很多的惡意競爭,也就是說,其他商家可能會通過不正當手段來惡意請求秒殺系統(tǒng),占用系統(tǒng)大量的帶寬和其他系統(tǒng)資源。此時,就需要使用風控系統(tǒng)等實現(xiàn)黑名單機制。為了簡單,也可以使用攔截器統(tǒng)計訪問頻次實現(xiàn)黑名單機制。
(4)驗證真實庫存是否足夠
系統(tǒng)需要驗證商品的真實庫存是否足夠,是否能夠支持本次秒殺活動的商品庫存量。
(5)扣減緩存中的庫存
在秒殺業(yè)務中,往往會將商品庫存等信息存放在緩存中,此時,還需要驗證秒殺活動使用的商品庫存是否足夠,并且需要扣減秒殺活動的商品庫存數(shù)量。
(6)計算秒殺的價格
由于在秒殺活動中,商品的秒殺價格和商品的真實價格存在差異,所以,需要計算商品的秒殺價格。
注意:如果在秒殺場景中,系統(tǒng)涉及的業(yè)務更加復雜的話,會涉及更多的業(yè)務操作,這里,我只是列舉出一些常見的業(yè)務操作。
2.提交訂單
(1)訂單入口
將用戶提交的訂單信息保存到數(shù)據庫中。
(2)扣減真實庫存
訂單入庫后,需要在商品的真實庫存中將本次成功下單的商品數(shù)量扣除。
如果我們使用上述流程開發(fā)了一個秒殺系統(tǒng),當用戶發(fā)起秒殺請求時,由于系統(tǒng)每個業(yè)務流程都是串行執(zhí)行的,整體上系統(tǒng)的性能不會太高,當并發(fā)量太高時,我們會為用戶彈出下面的排隊頁面,來提示用戶進行等待。
此時的排隊時間可能是15秒,也可能是30秒,甚至是更長時間。這就存在一個問題:在用戶發(fā)起秒殺請求到服務器返回結果的這段時間內,客戶端和服務器之間的連接不會被釋放,這就會占大量占用服務器的資源。
網上很多介紹如何實現(xiàn)秒殺系統(tǒng)的文章都是采用的這種方式,那么,這種方式能做秒殺系統(tǒng)嗎?答案是可以做,但是這種方式支撐的并發(fā)量并不是太高。此時,有些網友可能會問:我們公司就是這樣做的秒殺系統(tǒng)??!上線后一直在用,沒啥問題啊!我想說的是:使用同步下單方式確實可以做秒殺系統(tǒng),但是同步下單的性能不會太高。之所以你們公司采用同步下單的方式做秒殺系統(tǒng)沒出現(xiàn)大的問題,那是因為你們的秒殺系統(tǒng)的并發(fā)量沒達到一定的量級,也就是說,你們的秒殺系統(tǒng)的并發(fā)量其實并不高。
所以,很多所謂的秒殺系統(tǒng),存在著秒殺的業(yè)務,但是稱不上真正的秒殺系統(tǒng),原因就在于他們使用的是同步的下單流程,限制了系統(tǒng)的并發(fā)流量。之所以上線后沒出現(xiàn)太大的問題,是因為系統(tǒng)的并發(fā)量不高,不足以壓死整個系統(tǒng)。
如果12306、淘寶、天貓、京東、小米等大型商城的秒殺系統(tǒng)是這么玩的話,那么,他們的系統(tǒng)遲早會被玩死,他們的系統(tǒng)工程師不被開除才怪!所以,在秒殺系統(tǒng)中,這種同步處理下單的業(yè)務流程的方案是不可取的。
以上就是同步下單的整個流程操作,如果下單流程更加復雜的話,就會涉及到更多的業(yè)務操作。
異步下單流程
既然同步下單流程的秒殺系統(tǒng)稱不上真正的秒殺系統(tǒng),那我們就需要采用異步的下單流程了。異步的下單流程不會限制系統(tǒng)的高并發(fā)流量。
1.用戶發(fā)起秒殺請求
用戶發(fā)起秒殺請求后,商城服務會經過如下業(yè)務流程。
(1)檢測驗證碼是否正確
用戶發(fā)起秒殺請求時,會將驗證碼一同發(fā)送過來,系統(tǒng)會檢驗驗證碼是否有效,并且是否正確。
(2)是否限流
系統(tǒng)會對用戶的請求進行是否限流的判斷,這里,我們可以通過判斷消息隊列的長度來進行判斷。因為我們將用戶的請求放在了消息隊列中,消息隊列中堆積的是用戶的請求,我們可以根據當前消息隊列中存在的待處理的請求數(shù)量來判斷是否需要對用戶的請求進行限流處理。
例如,在秒殺活動中,我們出售1000件商品,此時在消息隊列中存在1000個請求,如果后續(xù)仍然有用戶發(fā)起秒殺請求,則后續(xù)的請求我們可以不再處理,直接向用戶返回商品已售完的提示。
所以,使用限流后,我們可以更快的處理用戶的請求和釋放連接的資源。
(3)發(fā)送MQ
用戶的秒殺請求通過前面的驗證后,我們就可以將用戶的請求參數(shù)等信息發(fā)送到MQ中進行異步處理,同時,向用戶響應結果信息。在商城服務中,會有專門的異步任務處理模塊來消費消息隊列中的請求,并處理后續(xù)的異步流程。
在用戶發(fā)起秒殺請求時,異步下單流程比同步下單流程處理的業(yè)務操作更少,它將后續(xù)的操作通過MQ發(fā)送給異步處理模塊進行處理,并迅速向用戶返回響應結果,釋放請求連接。
2.異步處理
我們可以將下單流程的如下操作進行異步處理。
(1)判斷活動是否已經結束
(2)判斷本次請求是否處于系統(tǒng)黑名單,為了防止電商領域同行的惡意競爭可以為系統(tǒng)增加黑名單機制,將惡意的請求放入系統(tǒng)的黑名單中??梢允褂脭r截器統(tǒng)計訪問頻次來實現(xiàn)。
(3)扣減緩存中的秒殺商品的庫存數(shù)量。
(4)生成秒殺Token,這個Token是綁定當前用戶和當前秒殺活動的,只有生成了秒殺Token的請求才有資格進行秒殺活動。
這里我們引入了異步處理機制,在異步處理中,系統(tǒng)使用多少資源,分配多少線程來處理相應的任務,是可以進行控制的。
3.短輪詢查詢秒殺結果
這里,可以采取客戶端短輪詢查詢是否獲得秒殺資格的方案。例如,客戶端可以每隔3秒鐘輪詢請求服務器,查詢是否獲得秒殺資格,這里,我們在服務器的處理就是判斷當前用戶是否存在秒殺Token,如果服務器為當前用戶生成了秒殺Token,則當前用戶存在秒殺資格。否則繼續(xù)輪詢查詢,直到超時或者服務器返回商品已售完或者無秒殺資格等信息為止。
采用短輪詢查詢秒殺結果時,在頁面上我們同樣可以提示用戶排隊處理中,但是此時客戶端會每隔幾秒輪詢服務器查詢秒殺資格的狀態(tài),相比于同步下單流程來說,無需長時間占用請求連接。
此時,可能會有網友會問:采用短輪詢查詢的方式,會不會存在直到超時也查詢不到是否具有秒殺資格的狀態(tài)呢?答案是:有可能! 這里我們試想一下秒殺的真實場景,商家參加秒殺活動本質上不是為了賺錢,而是提升商品的銷量和商家的知名度,吸引更多的用戶來買自己的商品。所以,我們不必保證用戶能夠100%的查詢到是否具有秒殺資格的狀態(tài)。
4.秒殺結算
(1)驗證下單Token
客戶端提交秒殺結算時,會將秒殺Token一同提交到服務器,商城服務會驗證當前的秒殺Token是否有效。
(2)加入秒殺購物車
商城服務在驗證秒殺Token合法并有效后,會將用戶秒殺的商品添加到秒殺購物車。
5.提交訂單
(1)訂單入庫
將用戶提交的訂單信息保存到數(shù)據庫中。
(2)刪除Token
秒殺商品訂單入庫成功后,刪除秒殺Token。
這里大家可以思考一個問題:我們?yōu)槭裁粗辉诋惒较聠瘟鞒痰姆凵糠植捎卯惒教幚?,而沒有在其他部分采取異步削峰和填谷的措施呢?
這是因為在異步下單流程的設計中,無論是在產品設計上還是在接口設計上,我們在用戶發(fā)起秒殺請求階段對用戶的請求進行了限流操作,可以說,系統(tǒng)的限流操作是非常前置的。在用戶發(fā)起秒殺請求時進行了限流,系統(tǒng)的高峰流量已經被平滑解決了,再往后走,其實系統(tǒng)的并發(fā)量和系統(tǒng)流量并不是非常高了。
所以,網上很多的文章和帖子中在介紹秒殺系統(tǒng)時,說是在下單時使用異步削峰來進行一些限流操作,那都是在扯淡!因為下單操作在整個秒殺系統(tǒng)的流程中屬于比較靠后的操作了,限流操作一定要前置處理,在秒殺業(yè)務后面的流程中做限流操作是沒啥卵用的。
高并發(fā)“黑科技”與致勝奇招
假設,在秒殺系統(tǒng)中我們使用Redis實現(xiàn)緩存,假設Redis的讀寫并發(fā)量在5萬左右。我們的商城秒殺業(yè)務需要支持的并發(fā)量在100萬左右。如果這100萬的并發(fā)全部打入Redis中,Redis很可能就會掛掉,那么,我們如何解決這個問題呢?接下來,我們就一起來探討這個問題。
在高并發(fā)的秒殺系統(tǒng)中,如果采用Redis緩存數(shù)據,則Redis緩存的并發(fā)處理能力是關鍵,因為很多的前綴操作都需要訪問Redis。而異步削峰只是基本的操作,關鍵還是要保證Redis的并發(fā)處理能力。
解決這個問題的關鍵思想就是:分而治之,將商品庫存分開放。
暗度陳倉
我們在Redis中存儲秒殺商品的庫存數(shù)量時,可以將秒殺商品的庫存進行“分割”存儲來提升Redis的讀寫并發(fā)量。
例如,原來的秒殺商品的id為10001,庫存為1000件,在Redis中的存儲為(10001, 1000),我們將原有的庫存分割為5份,則每份的庫存為200件,此時,我們在Redia中存儲的信息為(10001_0, 200),(10001_1, 200),(10001_2, 200),(10001_3, 200),(10001_4, 200)。
此時,我們將庫存進行分割后,每個分割后的庫存使用商品id加上一個數(shù)字標識來存儲,這樣,在對存儲商品庫存的每個Key進行Hash運算時,得出的Hash結果是不同的,這就說明,存儲商品庫存的Key有很大概率不在Redis的同一個槽位中,這就能夠提升Redis處理請求的性能和并發(fā)量。
分割庫存后,我們還需要在Redis中存儲一份商品id和分割庫存后的Key的映射關系,此時映射關系的Key為商品的id,也就是10001,Value為分割庫存后存儲庫存信息的Key,也就是10001_0,10001_1,10001_2,10001_3,10001_4。在Redis中我們可以使用List來存儲這些值。
在真正處理庫存信息時,我們可以先從Redis中查詢出秒殺商品對應的分割庫存后的所有Key,同時使用AtomicLong來記錄當前的請求數(shù)量,使用請求數(shù)量對從Redia中查詢出的秒殺商品對應的分割庫存后的所有Key的長度進行求模運算,得出的結果為0,1,2,3,4。再在前面拼接上商品id就可以得出真正的庫存緩存的Key。此時,就可以根據這個Key直接到Redis中獲取相應的庫存信息。
移花接木
在高并發(fā)業(yè)務場景中,我們可以直接使用Lua腳本庫(OpenResty)從負載均衡層直接訪問緩存。
這里,我們思考一個場景:如果在秒殺業(yè)務場景中,秒殺的商品被瞬間搶購一空。此時,用戶再發(fā)起秒殺請求時,如果系統(tǒng)由負載均衡層請求應用層的各個服務,再由應用層的各個服務訪問緩存和數(shù)據庫,其實,本質上已經沒有任何意義了,因為商品已經賣完了,再通過系統(tǒng)的應用層進行層層校驗已經沒有太多意義了?。《鴳脤拥牟l(fā)訪問量是以百為單位的,這又在一定程度上會降低系統(tǒng)的并發(fā)度。
為了解決這個問題,此時,我們可以在系統(tǒng)的負載均衡層取出用戶發(fā)送請求時攜帶的用戶id,商品id和秒殺活動id等信息,直接通過Lua腳本等技術來訪問緩存中的庫存信息。如果秒殺商品的庫存小于或者等于0,則直接返回用戶商品已售完的提示信息,而不用再經過應用層的層層校驗了。 針對這個架構,我們可以參見本文中的電商系統(tǒng)的架構圖(正文開始的第一張圖)。
寫在最后
最后,附上并發(fā)編程需要掌握的核心技能知識圖,祝大家在學習并發(fā)編程時,少走彎路。
后記:
記?。?/span>你比別人強的地方,不是你做過多少年的CRUD工作,而是你比別人掌握了更多深入的技能。不要總停留在CRUD的表面工作,理解并掌握底層原理并熟悉源碼實現(xiàn),并形成自己的抽象思維能力,做到靈活運用,才是你突破瓶頸,脫穎而出的重要方向!
你在刷抖音,玩游戲的時候,別人都在這里學習,成長,提升,人與人最大的差距其實就是思維。你可能不信,優(yōu)秀的人,總是在一起。。
特別推薦一個分享架構+算法的優(yōu)質內容,還沒關注的小伙伴,可以長按關注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!