高并發(fā)核心編程SpringCloud+Nginx秒殺實戰(zhàn),秒殺系統(tǒng)的系統(tǒng)架構(gòu)
秒殺系統(tǒng)的系統(tǒng)架構(gòu)
本節(jié)分多個維度介紹crazy-springcloud開發(fā)腳手架的架構(gòu),包括分層架構(gòu)、限流架構(gòu)、分布式鎖架構(gòu)、削峰的架構(gòu)。
秒殺的分層架構(gòu)
從分層的角度來說,秒殺系統(tǒng)架構(gòu)可以分成3層,大致如下:
(1)客戶端:負責內(nèi)容提速和交互控制。
(2)接入層:負責認證、負載均衡、限流。
(3)業(yè)務層:負責保障秒殺數(shù)據(jù)的一致性。
1.客戶端負責內(nèi)容提速和交互控制
客戶端需要完成秒殺商品的靜態(tài)化展示。無論是在桌面瀏覽器還是在移動端App展示秒殺商品,秒殺商品的圖片和文字元素都需要盡可能靜態(tài)化,盡量減少動態(tài)元素。這樣就可以通過CDN來提速和抗峰值。
另外,在客戶端這一層的用戶交互上需要具備一定的控制用戶行為和禁止重復秒殺的能力。比如,當用戶提交秒殺請求之后,可以將秒殺按鈕置灰,禁止重復提交。
2.接入層負責認證、負載均衡、限流
秒殺系統(tǒng)的特點是并發(fā)量極大,但實際的優(yōu)惠商品有限,秒殺成功的請求數(shù)量很少,如果不在接入層進行攔截,大量請求就會造成數(shù)據(jù)庫連接耗盡、服務端線程耗盡,導致整體雪崩。因此,必須在接入層進行用戶認證、負載均衡、接口限流。
對于總流量較小的系統(tǒng),可以在內(nèi)部網(wǎng)關(guān)(如Zuul)完成用戶認證、負載均衡、接口限流的功能,具體的分層架構(gòu)如圖10-2所示。

圖10-2 在內(nèi)部網(wǎng)關(guān)(如Zuul)完成認證、負載均衡、接口限流
對于總流量較大的系統(tǒng)會有一層甚至多層外部網(wǎng)關(guān),因此限流的職責會從內(nèi)部網(wǎng)關(guān)剝離到外部網(wǎng)關(guān),內(nèi)部網(wǎng)關(guān)(如Zuul)仍然具備權(quán)限認證、負載均衡的能力,具體的分層架構(gòu)如圖10-3所示。

圖10-3 外部網(wǎng)關(guān)與內(nèi)部網(wǎng)關(guān)相結(jié)合完成權(quán)限認證、負載均衡、接口限 流
3.業(yè)務層負責保障數(shù)據(jù)一致性
秒殺的業(yè)務邏輯主要是下訂單和減庫存,都是數(shù)據(jù)庫操作。大家都知道,數(shù)據(jù)庫層只能承擔“能力范圍內(nèi)”的訪問請求,既是非常脆弱的一層,又是需要進行事務保護的一層。在業(yè)務層還需要防止超出庫存的秒殺(超賣和少賣),為了安全起見,可以使用分布式鎖對秒殺的數(shù)據(jù)庫操作進行保護。
?秒殺的限流架構(gòu)
前面提到,秒殺系統(tǒng)中的秒殺商品總是有限的。除此之外,服務節(jié)點的處理能力、數(shù)據(jù)庫的處理能力也是有限的,因此需要根據(jù)系統(tǒng)的負載能力進行秒殺限流。
總體來說,在接入層可以進行兩個級別的限流策略:應用級別的限流和接口級別的限流。
什么是應用級別的限流策略呢?對于整個應用系統(tǒng)來說,一定會有一個QPS的極限值,如果超了極限值,整個應用就會不響應或響應得非常慢。因此,需要在整個應用的維度做好應用級別的限流配置。應用級別的限流應該配置在最頂層的反向代理,具體如圖10-4所示。

圖10-4 應用級別的限流
應用級別的流量限制可以通過Nginx的limit_req_zone和limit_req兩個指令完成。假定要配置Nginx虛擬主機的限流規(guī)則為單IP限制為每秒1次請求,整個應用限制為每秒10次請求,那么具體的配置如下:
limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
limit_req_zone $server_name zone=perserver:1m rate=10r/s;
server {
...
limit_req zone=perip burst=5;
limit_req zone=perserver burst=10;
}什么是接口級別的限流策略呢?單個接口可能會有突發(fā)訪問情況,可能會由于突發(fā)訪問量太大造成系統(tǒng)崩潰,典型的就是本章所介紹的秒殺類接口。接口級別的限流就是配置單個接口的請求速率,是細粒度的限流。
接口級別的限流也可以通過Nginx的limit_req_zone和limit_req兩個指令配合完成,對獲取秒殺令牌的接口,同時進行用戶Id和商品Id限流的配置如下:
limit_req_zone $arg_goodId zone=pergood:10m rate=100r/s;
limit_req_zone $arg_userId zone=peruser:1m rate=1r/s;
server {
#lua:獲取秒殺token令牌
location = /seckill-provider/api/seckill/redis/token/v2 {
limit_req zone=peruser burst=5;
limit_req zone=pergood burst=10;
#獲取秒殺token lua腳本
content_by_lua_file luaScript/module/seckill/getToken.lua;
}
}以上定義了兩個限流規(guī)則:pergood和peruser:pergood規(guī)則根據(jù)請求參數(shù)的goodId值進行限流,同一個goodId值的限速為每秒100次請求;peruser規(guī)則根據(jù)請求參數(shù)的userId值進行限流,同一個userId值的限速為每秒1次請求。
但是,Nginx的限流指令只能在同一塊內(nèi)存區(qū)域有效,而在生產(chǎn)場景中秒殺的外部網(wǎng)關(guān)往往是采用多節(jié)點部署的,所以這就需要用到分布式限流組件。高性能的分布式限流組件可以使用Redis+Lua來開發(fā),京東的搶購就是使用Redis+Lua完成限流的,并且無論是Nginx外部網(wǎng)關(guān)還是Zuul內(nèi)部網(wǎng)關(guān),都可以使用Redis+Lua限流組件。
理論上,接入層的限流有多個維度:
(1)用戶維度的限流:在某一時間段內(nèi)只允許用戶提交一次請求,比如可以采取客戶端IP或者用戶ID作為限流的key。
(2)商品維度的限流:對于同一個搶購商品,在某個時間段內(nèi)只允許一定數(shù)量的請求進入,可以采取秒殺商品ID作為限流的key。
無論是哪個維度的限流,只要掌握其中的一個,其他維度的限流在技術(shù)實現(xiàn)上都是差不多的。本書的秒殺練習使用的是接口級別的限流策略,在獲取秒殺令牌的REST接口時,針對每個秒殺商品的ID配置限流策略,限制每個商品ID每秒內(nèi)允許通過的請求次數(shù)。
如果大家對進行用戶維度的限流感興趣,可以自行修改配置進行嘗試。
?秒殺的分布式鎖架構(gòu)
前面提到了超賣或少賣問題:比如10萬次請求同時發(fā)起秒殺請求,正常需要進行10萬次庫存扣減,但是由于某種原因,往往會造成多減庫存或者少減庫存,這就會出現(xiàn)超賣或少賣問題。
解決超賣或者少賣問題有效的辦法之一就是利用分布式鎖將對同一個商品的并行數(shù)據(jù)庫操作予以串行化。秒殺場景的分布式鎖應該具備如下條件:
(1)一個方法在同一時間只能被一個機器的一個線程執(zhí)行。
(2)高可用地獲取鎖與釋放鎖。
(3)高性能地獲取鎖與釋放鎖。
(4)具備可重入特性。
(5)具備鎖失效機制,防止死鎖。
(6)具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗。
常用的分布式鎖有兩種:ZooKeeper分布式鎖和Redis分布式鎖。如果使用ZooKeeper分布式鎖來保護秒殺的數(shù)據(jù)庫操作,那么它的架構(gòu)圖大致如圖10-5所示。

圖10-5 使用ZooKeeper分布式鎖來保護秒殺的數(shù)據(jù)庫操作
實際上,除了提供分布式鎖外,ZooKeeper還能提供高可靠的分布式計數(shù)器、高可靠的分布式ID生成器的基礎(chǔ)能力。ZooKeeper分布式計數(shù)器、分布式鎖、分布式ID生成器等基礎(chǔ)知識也是大家必須系統(tǒng)地學習和掌握的知識,但是不屬于在本書介紹的內(nèi)容,如果對這一塊不了解,可翻閱本書姊妹篇《Netty、Redis、ZooKeeper高并發(fā)實戰(zhàn)》。
ZooKeeper分布式鎖雖然高可靠,但是性能不高,不能滿足秒殺場景分布式鎖的第3個條件(高性能地獲取鎖與釋放鎖),所以在秒殺的場景建議使用Redis分布式鎖來保護秒殺的數(shù)據(jù)庫操作。
?秒殺的削峰架構(gòu)
通過接入網(wǎng)關(guān)的限流能夠攔截無效的刷單請求和超出預期的那部分請求,但是,當秒殺的訂單量很大時,比如有100萬商品需要參與秒殺,這時后端服務層和數(shù)據(jù)庫的并發(fā)請求壓力至少為100萬。這種請求下,需要使用消息隊列進行削峰。
削峰從本質(zhì)上來說就是更多地延緩用戶請求,以及層層過濾用戶的訪問需求,遵從“最后落地到數(shù)據(jù)庫的請求數(shù)要盡量少”的原則。通過消息隊列可以大大地緩沖瞬時流量,把同步的直接調(diào)用轉(zhuǎn)換成異步的間接推送,中間通過一個隊列在入口承接瞬時的流量洪峰,在出口平滑地將消息推送出去。消息隊列就像“水庫”一樣,攔蓄上游的洪水,削減進入下游河道的洪峰流量,從而達到減免洪水災害的目的。使用消息隊列對秒殺進行削峰的架構(gòu)如圖10-6所示。

圖10-6 使用消息隊列對秒殺進行削峰
對于秒殺消息的入隊可以直接在內(nèi)部網(wǎng)關(guān)完成。內(nèi)部網(wǎng)關(guān)在完成用戶的權(quán)限驗證、秒殺令牌的有效性驗證之后,將秒殺消息發(fā)往消息隊列即可。秒殺服務通過消息隊列的訂閱完成秒殺消息的消費。常用消息隊列系統(tǒng):Kafka、RocketMQ、ActiveMQ、RabbitMQ、ZeroMQ、MetaMQ等。本書的內(nèi)容主要聚焦在Spring Cloud和Nginx,對消息隊列這里不做過多的介紹,使用消息隊列進行削峰的秒殺實現(xiàn)版本可參見后續(xù)的瘋狂創(chuàng)客圈社群博客。
本文給大家講解的內(nèi)容是高并發(fā)核心編程,Spring Cloud+Nginx秒殺實戰(zhàn),秒殺系統(tǒng)的系統(tǒng)架構(gòu)
下篇文章給大家講解的是高并發(fā)核心編程,Spring Cloud+Nginx秒殺實戰(zhàn),秒殺業(yè)務的參考實現(xiàn);
覺得文章不錯的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;
感謝大家的支持!
本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學習更多的話可以到微信公眾號里找我,我等你哦。
