前言:學習自慕課網課程—《Java秒殺系統方案優化 高性能高并發實戰》,看完后自己開發了一遍,受益很多,便打算寫出來與大家一起分享,本文為自己做完后的專案總結與筆記,
技術堆疊:Spring Boot、Thymeleaf、MyBatis-Plus、Redis、RabbitMQ
壓測工具:JMeter

文章目錄
- 電商秒殺介紹
- 1 用戶認證
- 1.1 用戶登錄
- 1.2 共享 Session 知識
- 1.3 記錄用戶憑證
- 2 功能開發
- 2.1 商品串列
- 2.2 商品詳情
- 2.3 秒殺
- 2.4 訂單詳情
- 3 系統壓測
- 3.1 JMeter
- 3.2 自定義變數模擬多用戶
- 3.3 正式壓測
- 4 頁面優化
- 5 介面優化
- 5.1 解決超賣
- 5.1 Redis 預減庫存減少資料庫的訪問
- 5.2 記憶體標記減少 Redis 的訪問
- 5.3 RabbitMQ 異步下單
- 6 安全優化
- 6.1 秒殺介面地址隱藏
- 6.2 算術驗證碼
- 6.3 介面限流
電商秒殺介紹
秒殺是在 瞬間擊殺 的意思,放在電商中,就是網上競拍的一種方式;所謂“秒殺”,就是網路賣家發布一些超低價格的商品,所有買家在同一時間網上搶購的一種銷售方式,由于商品價格低廉,往往一上架就被搶購一空,有時只用一秒鐘,
從技術角度來講,秒殺主要解決兩個問題,一個是并發讀,一個是并發寫,
- 并發讀的核心優化理念是盡量減少用戶到服務端來“讀”資料,或者讓他們讀更少的資料;
- 并發寫的處理原則也一樣,它要求我們在資料庫層面獨立出來一個庫,做特殊的處理,
另外,電商秒殺系統也要保證高性能、資料一致性、系統高可用性等必要性能,因此實際專案的難度十分之大,會出現許多意料之外的情況,本文學習并總結自慕課網教程,已在前言中寫明,下面,就電商秒殺中的幾個核心知識點進行專案的練習,
1 用戶認證
1.1 用戶登錄
用戶資訊加密本專案中采用 MD5 這種加密方式,明文密碼需要二次 MD5 加密,當用戶注冊時,我們在前臺校驗輸入引數成功后,將密碼進行 MD5 加密傳輸到后臺,后臺經過處理邏輯判斷后,如果判定為這些輸入引數可以進行注冊,那么這時也應該將密碼再次加密存入資料庫中,只有兩次在傳輸時進行加密,才可以保證用戶輸入的資訊的相對安全的(MD5 存在弱點,也不是絕對不可以破解的),下面為后臺的兩次加密方法,在本專案中只用 backPassToDBPass 這個方法,前臺則也有一個類似的加密方法,用 JS 實作,
登錄功能中密碼的傳輸也是一樣的,



1.2 共享 Session 知識
分布式環境下 Session 不在服務器集群上互通,有以下幾種常見解決方案:
-
Session復制:優點是無需修改代碼,只需要修改 Tomcat 配置,缺點是 Session 同步傳輸占用內網帶寬多臺 Tomcat 同步性能指數級下降,Session 占用記憶體,無法有效水平擴展;
-
前端存盤:優點是不占用服務端記憶體,缺點是存在安全風險,資料大小受 cookie 限制,占用外網帶寬 ;
-
Session 粘滯:優點是無需修改代碼,服務端可以水平擴展,缺點是增加新機器,會重新Hash,導致重新登錄,應用重啟,需要重新登錄;
-
后端集中存盤:優點是安全,容易水平擴展,缺點是增加復雜度,需要修改代碼,
將用戶登錄的資訊存入 Redis 中,這樣如果是分布式系統就可以通過 Redis 來獲取用戶資訊并認證了,實作了分布式用戶認證,
1.3 記錄用戶憑證
將用戶登錄的資訊存入 Redis 中,這樣系統就可以通過 Redis 來獲取用戶資訊并認證了( redis 也可以用集群,用戶認證也可以用 JWT token 簽名實作用戶認證,不用 redis 等等都可以,這個專案只是側重于秒殺,而這里記錄用戶憑證資訊是為了之后模擬批量用戶進行秒殺測驗),
用戶初次登錄時


由于我們之后每個需要用戶認證的方法,都需要 @CookieValue(“userTicket”) String ticket ,因此我們每次都要拿到 ticket ,再去 redis 中判斷是否存在用戶,等等操作,所以,我們直接將這一步 AOP 化,也就是在直接用 Spring MVC 的引數決議器將其在方法引數中直接完成判斷



之后需要用戶資訊的方法,我們這樣寫就可以了

2 功能開發
2.1 商品串列
單獨創建秒殺表和秒殺訂單表

秒殺頁面

2.2 商品詳情

2.3 秒殺
秒殺活動未開始前,不展示驗證碼,立即秒殺按鈕不可點擊

秒殺活動進行中,展示驗證碼,立即秒殺按鈕可點擊

秒殺活動結束后,不展示驗證碼,立即秒殺按鈕不可點擊


秒殺初步實作思路如下(本文是按照慕課教程循序漸進地撰寫代碼的,因此這里故意不處理并發等問題,讓問題暴露出來,方便后續優化的進行),首先一個用戶秒殺商品,那么第一件是就是要判斷是否還有沒有秒殺庫存,如果有的話那么也要判斷這個用戶在本次活動中是否已經秒殺成功過,如果有庫存,未重復搶購,那么才可以進行秒殺,而秒殺的業務邏輯是先生成訂單,訂單為未支付狀態,資料庫中庫存減1,在訂單過期前,用戶支付成功,那么則生成真正有效訂單(生成秒殺訂單和統一訂單,由于我們要處理高并發問題,所以單獨抽出秒殺訂單表,但秒殺訂單仍屬于統一訂單表),
秒殺代碼初步實作

生成訂單的實作

2.4 訂單詳情
秒殺成功,資料庫的庫存也是在我們的預期變化內,這在單機用戶下,是沒有問題的,多機大量用戶時,就是出現超買超賣問題,我們后續進行演示,

3 系統壓測
3.1 JMeter
Apache JMeter 是 Apache 組織基于 Java 開發的壓力測驗工具,用于對軟體做壓力測驗,我們接下來用 JMeter 模擬大量用戶進行商品的秒殺的場景,
3.2 自定義變數模擬多用戶
我們首先在資料庫的用戶表中插入 5000 個用戶,用代碼回圈實作即可,同時也將兩個產品的可秒殺數量都設為10,清空訂單表,方便之后的測驗,

Redis 中

創建 5000 個用戶執行緒,并攜帶其 Cookie,進行秒殺測驗

3.3 正式壓測
執行壓測,等待測驗完成

秒殺商品表出現了超賣現象,庫存都成負數了

下圖的秒殺訂單表就更不用說了,已經五百多條資料了,然而實際我們秒殺庫存只有 10 件,因此,秒殺問題如果處理不好,后果是非常嚴重的,接下來我們展示如何解決超賣問題,

4 頁面優化
由于此慕課教程的專案是按單體專案開發的,而現在很多專案已是前后端分離,這里情況不一樣了,按這個專案的頁面優化來說,那就是將可以靜態化的頁面靜態化,然后直接放入 redis,當然可以 CDN 優化,這里就不展開說了,
5 介面優化
5.1 解決超賣
我們在生成訂單的方法中,再加一個庫存大于 0 的判斷,如果大于 0 ,我們就不應該生成訂單(我們之前是在秒殺時判斷的庫存是否為正數,此處我們再次在生成訂單部分再判斷),大于 0 的判斷,和是否更新這兩個操作應屬于在一個原子操作內進行,

同時,我們在生成訂單時,也將訂單資訊存入 redis 中,方便之后在秒殺這個大流量的部分判斷庫存是否大于 0 時,直接在 redis 快取中獲取是否重復搶購,減輕資料庫的壓力

為了應對重負搶購的情況,我們再在資料庫中設定唯一索引,欄位為用戶 id 與秒殺商品 id 的組合,這樣,在最基礎的資料層將不會出現重復搶購的記錄

再出初始化我們的測驗資料,進行壓測,發現就不會有超賣問題了
秒殺商品表

秒殺訂單表

訂單表

5.1 Redis 預減庫存減少資料庫的訪問
5.2 記憶體標記減少 Redis 的訪問
5.3 RabbitMQ 異步下單
秒殺成功后,我們將秒殺資訊以訊息佇列的形式發送給訂單生成模塊,讓其生成訂單

發送方

接收方




6 安全優化
6.1 秒殺介面地址隱藏
秒殺開始之前,先去請求介面獲取秒殺地址,防止有人預先知道介面后提前準備好不斷去請求,


6.2 算術驗證碼
能起到一定的瞬間壓力緩沖作用,因為用戶有人算得快,有人算得慢,也能一定程度抵御腳本,不過現在 OCR 技術確實已經很強了,

6.3 介面限流
可以用寫一個通用的限流注解,例如同一客戶端多長時間范圍內,多少次請求就被限流的引數,然后再在攔截器中用反射來獲取限流注解,決議其引數,然后去 redis 中判斷是否需要限流等等,


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/433342.html
標籤:其他
