基於Springboot高併發商品限時秒殺系統

系統介紹

本系統是使用SpringBoot開發的高併發限時搶購秒殺系統,除了實現基本的登錄、查看商品列表、秒殺、下單等功能,項目中還針對高併發情況實現了系統緩存、降級和限流。

開發工具

IntelliJ IDEA + Navicat + Sublime Text3 + Git + Chrome

壓測工具

JMeter

開發技術

前端技術 :Bootstrap + jQuery + Thymeleaf

後端技術 :SpringBoot + MyBatis + MySQL

中間件技術 : Druid + Redis + RabbitMQ + Guava

秒殺優化方向

  1. 將請求儘量攔截在系統上游:傳統秒殺系統之所以掛,請求都壓倒了後端數據層,數據讀寫鎖衝突嚴重,幾乎所有請求都超時,流量雖大,下單成功的有效流量甚小,我們可以通過限流、降級等措施來最大化減少對數據庫的訪問,從而保護系統。

  2. 充分利用緩存:秒殺商品是一個典型的讀多寫少的應用場景,充分利用緩存將大大提高併發量

實現技術點

1. 兩次MD5加密

將用戶輸入的密碼和固定Salt通過MD5加密生成第一次加密後的密碼,再講該密碼和隨機生成的Salt通過MD5進行第二次加密,最後將第二次加密後的密碼和第一次的固定Salt存數據庫

好處:

  1. 第一次作用:防止用戶明文密碼在網絡進行傳輸
  2. 第二次作用:防止數據庫被盜,避免通過MD5反推出密碼,雙重保險

2. session共享

驗證用戶賬號密碼都正確情況下,通過UUID生成唯一id作爲token,再將token作爲key、用戶信息作爲value模擬session存儲到redis,同時將token存儲到cookie,保存登錄狀態

好處: 在分佈式集羣情況下,服務器間需要同步,定時同步各個服務器的session信息,會因爲延遲到導致session不一致,使用redis把session數據集中存儲起來,解決session不一致問題。

3. JSR303自定義參數驗證

使用JSR303自定義校驗器,實現對用戶賬號、密碼的驗證,使得驗證邏輯從業務代碼中脫離出來。

4. 全局異常統一處理

通過攔截所有異常,對各種異常進行相應的處理,當遇到異常就逐層上拋,一直拋到最終由一個統一的、專門負責異常處理的地方處理,這有利於對異常的維護。

5. 頁面緩存 + 對象緩存

  1. 頁面緩存:通過在手動渲染得到的html頁面緩存到redis
  2. 對象緩存:包括對用戶信息、商品信息、訂單信息和token等數據進行緩存,利用緩存來減少對數據庫的訪問,大大加快查詢速度。

6. 頁面靜態化

對商品詳情和訂單詳情進行頁面靜態化處理,頁面是存在html,動態數據是通過接口從服務端獲取,實現前後端分離,靜態頁面無需連接數據庫打開速度較動態頁面會有明顯提高

7. 本地標記 + redis預處理 + RabbitMQ異步下單 + 客戶端輪詢

描述:通過三級緩衝保護,1、本地標記 2、redis預處理 3、RabbitMQ異步下單,最後纔會訪問數據庫,這樣做是爲了最大力度減少對數據庫的訪問。

實現:

  1. 在秒殺階段使用本地標記對用戶秒殺過的商品做標記,若被標記過直接返回重複秒殺,未被標記才查詢redis,通過本地標記來減少對redis的訪問
  2. 搶購開始前,將商品和庫存數據同步到redis中,所有的搶購操作都在redis中進行處理,通過Redis預減少庫存減少數據庫訪問
  3. 爲了保護系統不受高流量的衝擊而導致系統崩潰的問題,使用RabbitMQ用異步隊列處理下單,實際做了一層緩衝保護,做了一個窗口模型,窗口模型會實時的刷新用戶秒殺的狀態。
  4. client端用js輪詢一個接口,用來獲取處理狀態

8. 解決超賣

描述:比如某商品的庫存爲1,此時用戶1和用戶2併發購買該商品,用戶1提交訂單後該商品的庫存被修改爲0,而此時用戶2並不知道的情況下提交訂單,該商品的庫存再次被修改爲-1,這就是超賣現象

實現:

  1. 對庫存更新時,先對庫存判斷,只有當庫存大於0才能更新庫存
  2. 對用戶id和商品id建立一個唯一索引,通過這種約束避免同一用戶發同時兩個請求秒殺到兩件相同商品
  3. 實現樂觀鎖,給商品信息表增加一個version字段,爲每一條數據加上版本。每次更新的時候version+1,並且更新時候帶上版本號,當提交前版本號等於更新前版本號,說明此時沒有被其他線程影響到,正常更新,如果衝突了則不會進行提交更新。當庫存是足夠的情況下發生樂觀鎖衝突就進行一定次數的重試。

9. 使用數學公式驗證碼

描述:點擊秒殺前,先讓用戶輸入數學公式驗證碼,驗證正確才能進行秒殺。

好處:

  1. 防止惡意的機器人和爬蟲
  2. 分散用戶的請求

實現:

  1. 前端通過把商品id作爲參數調用服務端創建驗證碼接口
  2. 服務端根據前端傳過來的商品id和用戶id生成驗證碼,並將商品id+用戶id作爲key,生成的驗證碼作爲value存入redis,同時將生成的驗證碼輸入圖片寫入imageIO讓前端展示
  3. 將用戶輸入的驗證碼與根據商品id+用戶id從redis查詢到的驗證碼對比,相同就返回驗證成功,進入秒殺;不同或從redis查詢的驗證碼爲空都返回驗證失敗,刷新驗證碼重試

10. 使用RateLimiter實現限流

描述:當我們去秒殺一些商品時,此時可能會因爲訪問量太大而導致系統崩潰,此時要使用限流來進行限制訪問量,當達到限流閥值,後續請求會被降級;降級後的處理方案可以是:返回排隊頁面(高峯期訪問太頻繁,等一會重試)、錯誤頁等。

實現:項目使用RateLimiter來實現限流,RateLimiter是guava提供的基於令牌桶算法的限流實現類,通過調整生成token的速率來限制用戶頻繁訪問秒殺頁面,從而達到防止超大流量沖垮系統。(令牌桶算法的原理是系統會以一個恆定的速度往桶裏放入令牌,而如果請求需要被處理,則需要先從桶裏獲取一個令牌,當桶裏沒有令牌可取時,則拒絕服務)

壓測效果

優化前 :開啓1000個線程循環10次同時訪問,QPS = 423  優化後:QPS = 2501 


本項目是學習了imooc網視頻之後的個人理解和知識彙總,學習鏈接:https://coding.imooc.com/class/168.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章