目錄
1.抽獎系統的背景引入node
2.結合具體業務需求分析抽獎系統數據庫
3.一個未通過優化的系統架構多線程
4.負載均衡層的限流架構
5.Tomcat線程數量的優化併發
6.基於Redis實現抽獎業務邏輯負載均衡
7.發放禮品環節進行限流削峯高併發
8.系統架構設計總結性能
一、抽獎系統的背景引入
本文給你們分享一個以前經歷過的抽獎系統的流量削峯架構的設計方案。優化
抽獎、搶紅包、秒殺,這類系統其實都有一些共同的特色,那就是在某個時間點會瞬間涌入大量的人來點擊系統,給系統形成瞬間高於平時百倍、千倍甚至幾十萬倍的流量壓力。網站
好比抽獎,有一種場景:某個網站或者APP規定好了在某個時間點,全部人均可以參與抽獎,那麼可能百萬級的用戶會蹲守在那個時間點,到時間你們一塊兒參與這個抽獎。
搶紅包,多是某個電視節目上,忽然說掃碼能夠搶紅包,那麼電視機前可能千萬級的用戶會瞬間一塊兒打開手機掃碼搶紅包。
秒殺更是如此,所謂秒殺,意思是讓你們都在電腦前等着,在某個時間忽然就能夠搶購某個限量的商品
好比某個手機平時賣5999,如今限量100臺價格才2999,50%的折扣,可能百萬級的用戶就會蹲守在電腦前在好比凌晨12點一塊兒點擊按鈕搶購這款手機。
相似的場景其實如今是不少的,那麼本文就用一個抽獎系統舉例,說說應對這種瞬時超高併發的流量,應該如何設計流量削峯的架構來應對,才能保證系統不會忽然跨掉?
二、結合具體業務需求分析抽獎系統
假設如今有一個抽獎的業務場景,用戶在某個時間能夠參與抽獎,好比一共有1萬個獎,獎品就是某個禮物。
而後參與抽獎的用戶可能有幾十萬,一瞬間可能幾十萬請求涌入過來,接着瞬間其中1萬人中獎了,剩餘的人都是沒中獎的。而後中獎的1萬人的請求會聯動調用禮品服務,完成這1萬中獎人的禮品發放。
簡單來講,需求場景就是如此,然而這裏就有不少的地方值得優化了。
三、一個未通過優化的系統架構
先來看一個未通過任何優化的系統架構,簡單來講就是有一個負載均衡的設備會把瞬間涌入的超高併發的流量轉發到後臺的抽獎服務上。
這個抽獎服務就是用普通的Tomcat來部署的,裏面實現了具體的抽獎邏輯,假設剛開始最常規的抽獎邏輯是基於MySQL來實現的,接着就是基於Tomcat部署的禮品服務,抽獎服務若是發現中獎了須要調用禮品服務去發放禮品。
以下圖所示:
四、負載均衡層的限流
4.1 防止用戶重複抽獎
首先第一次在負載均衡層能夠作的事情,就是防止重複抽獎。
咱們能夠在負載均衡設備中作一些配置,判斷若是同一個用戶在1分鐘以內屢次發送請求來進行抽獎,就認爲是惡意重複抽獎,或者是他們本身寫的腳本在刷獎,這種流量一概認爲是無效流量,在負載均衡設備那個層次就給直接屏蔽掉。
舉個例子,好比有幾十萬用戶瞬間同時抽獎,最多其實也就幾十萬請求而已,可是若是有人重複抽獎或者是寫腳本刷獎,那可能瞬間涌入的是幾百萬的請求,就不是幾十萬的請求了,因此這裏就能夠把無效流量給攔截掉。
以下圖所示:
4.2 所有開獎後暴力攔截流量
其實秒殺、搶紅包、抽獎,這類系統有一個共同的特色,那就是假設有50萬請求涌入進來,可能前5萬請求就直接把事兒幹完了,甚至是前500請求就把事兒幹完了,後續的幾十萬流量是無效的,不須要讓他們進入後臺系統執行業務邏輯了。
什麼意思呢?
舉個例子,秒殺商品,假設有50萬人搶一個特價手機,人家就準備了100臺手機,那麼50萬請求瞬間涌入,其實前500個請求就把手機搶完了,後續的幾十萬請求不必讓他轉發到Tomcat服務中去執行秒殺業務邏輯了,不是嗎?
抽獎、紅包都是同樣的 ,可能50萬請求涌入,可是前1萬個請求就把獎品都抽完了,或者把紅包都搶完了,後續的流量其實已經不須要放到Tomcat抽獎服務上去了,直接暴力攔截返回抽獎結束就能夠了。
這樣的話,其實在負載均衡這一層(能夠考慮用Nginx之類的來實現)就能夠攔截掉99%的無效流量。
因此必須讓抽獎服務跟負載均衡之間有一個狀態共享的機制。
就是說抽獎服務一旦所有開獎完畢,直接更新一個共享狀態。而後負載均衡感知到了以後,後續請求所有攔截掉返回一個抽獎結束的標識就能夠了。
這麼作可能就會作到50萬人一塊兒請求,結果就可能2萬請求到了後臺的Tomcat抽獎服務中,48萬請求直接攔截掉了。
咱們能夠基於Redis來實現這種共享抽獎狀態,它很是輕量級,很適合兩個層次的系統的共享訪問。
固然其實用ZooKeeper也是能夠的,在負載均衡層能夠基於zk客戶端監聽某個znode節點狀態。一旦抽獎結束,抽獎服務更新zk狀態,負載均衡層會感知到。
下圖展現了上述所說的過程:
五、Tomcat線程數量的優化
其次就是對於線上生產環境的Tomcat,有一個相當重要的參數是須要根據本身的狀況調節好的,那就是他的工做線程數量。
衆所周知,對於進入Tomcat的每一個請求,其實都會交給一個獨立的工做線程來進行處理,那麼Tomcat有多少線程,就決定了併發請求處理的能力。
可是這個線程數量是須要通過壓測來進行判斷的,由於每一個線程都會處理一個請求,這個請求又須要訪問數據庫之類的外部系統,因此不是每一個系統的參數均可以同樣的,須要本身對系統進行壓測。
可是給一個經驗值的話,Tomcat的線程數量不宜過多。由於線程過多,普通虛擬機的CPU是扛不住的,反而會致使機器CPU負載太高,最終崩潰。
同時,Tomcat的線程數量也不宜太少,由於若是就100個線程,那麼會致使沒法充分利用Tomcat的線程資源和機器的CPU資源。
因此通常來講,Tomcat線程數量在200~500之間都是能夠的,可是具體多少須要本身壓測一下,不斷的調節參數,看具體的CPU負載以及線程執行請求的一個效率。
在CPU負載尚可,以及請求執行性能正常的狀況下,儘量提升一些線程數量。
可是若是到一個臨界值,發現機器負載太高,並且線程處理請求的速度開始降低,說明這臺機扛不住這麼多線程併發執行處理請求了,此時就不能繼續上調線程數量了。
六、基於Redis實現抽獎業務邏輯
如今問題又來了,雖然在負載均衡那個層面,已經把好比50萬流量中的48萬都攔截掉了,可是可能仍是會有2萬流量進入抽獎服務
此時抽獎服務天然是能夠多機器來部署的,好比假設一臺Tomcat能夠抗500請求,那麼2萬併發就是40臺機器。
若是你是基於雲平臺來部署系統的,搞活動臨時租用一批機器就能夠了,活動結束了機器立馬能夠釋放掉,如今雲平臺都很方便。
可是有個問題,你的數據庫MySQL能抗住2萬的併發請求嗎?
若是你基於MySQL來實現核心的抽獎業務邏輯,40個Tomcat部署的抽獎服務頻繁對MySQL進行增刪改查,這一個MySQL實例也是很難抗住的。
因此此時還得把MySQL給替換成Redis,一般這種場景下,建議是基於Redis來實現核心的業務邏輯。
Redis單機抗2萬併發那是很輕鬆的一件事情,因此在這裏又須要作進一步的優化。以下圖:
七、發放禮品環節進行限流削峯
接着問題又來了,假設抽獎服務在2萬請求中有1萬請求抽中了獎品,那麼勢必會形成抽獎服務對禮品服務調用1萬次。
禮品服務假設也是優化後的Tomcat,能夠抗500併發,難道禮品服務也要去部署20臺機器嗎?
其實這是不必的,由於抽獎以後徹底可讓禮品服務在後臺慢慢的把中獎的禮品給發放出去,不須要一會兒就立馬對1萬個請求完成禮品的發放邏輯。
因此這裏能夠在抽獎服務和禮品服務之間,引入消息中間件,進行限流削峯。
也就是說,抽獎服務把中獎信息發送到MQ,而後禮品服務假設就部署兩個Tomcat,慢慢的從MQ中消費中獎消息,而後慢慢完成1完禮品的發放就能夠了。
假設兩個禮品服務實例每秒能夠完成100個禮品的發放,那麼1萬個禮品也就是延遲100秒發放完畢罷了。
也就是你抽獎以後,可能過了一兩分鐘,會看到本身的禮品發放的一些物流配送的進度之類的。
並且禮品服務可能須要在MySQL數據庫中作不少增刪改查的操做,好比插入中獎紀錄,而後進行禮品發貨等等。
此時由於禮品服務就2個Tomcat實例,因此對MySQL的併發讀寫不會過高,那麼數據庫層面也是能夠抗住的。
整個過程,以下圖所示:
八、系統架構設計總結
其實對於商品秒殺、抽獎活動、搶紅包類的系統而言,架構設計的思路不少都是相似的,核心思路都是對於這種瞬時超高流量的系統,儘量在負載均衡層就把99%的無效流量攔截掉
而後在1%的流量進入核心業務服務後,此時每秒併發仍是可能會上萬,那麼能夠基於Redis實現核心業務邏輯 ,抗住上萬併發。
最後對於相似秒殺商品發貨、抽獎商品發貨、紅包資金轉帳之類的很是耗時的操做,徹底能夠基於MQ來限流削峯,後臺有一個服務慢慢執行便可。