源碼地址:https://github.com/lahhass/miaosha_idea
秒殺業務流程
用戶點擊商品列表頁中的商品,進入商品詳情頁,如果秒殺成功,則跳轉訂單詳情頁。
- 瞬間的併發非常大,系統很可能出現問題,瓶頸在數據庫(加緩存,異步化來減輕數據庫壓力,防止直接穿透到數據庫)
秒殺架構設計理念
- 限流: 鑑於只有少部分用戶能夠秒殺成功,所以要限制大部分流量,只允許少部分流量進入服務後端。
- 削峯:對於秒殺系統瞬時會有大量用戶湧入,所以在搶購一開始會有很高的瞬間峯值。高峯值流量是壓垮系統很重要的原因,所以如何把瞬間的高流量變成一段時間平穩的流量也是設計秒殺系統很重要的思路。實現削峯的常用的方法有利用緩存和消息中間件等技術。
- 異步處理:秒殺系統是一個高併發系統,採用異步處理模式可以極大地提高系統併發量,其實異步處理就是削峯的一種實現方式。
- 內存緩存:秒殺系統最大的瓶頸一般都是數據庫讀寫,由於數據庫讀寫屬於磁盤IO,性能很低,如果能夠把部分數據或業務邏輯轉移到內存緩存,效率會有極大地提升。
- 可拓展:當然如果我們想支持更多用戶,更大的併發,最好就將系統設計成彈性可拓展的,如果流量來了,拓展機器就好了。像淘寶、京東等雙十一活動時會增加大量機器應對交易高峯。
秒殺系統架構設計思路
- 將請求攔截在系統上游,降低下游壓力:秒殺系統特點是併發量極大,但實際秒殺成功的請求數量卻很少,所以如果不在前端攔截很可能造成數據庫讀寫鎖衝突,最終請求超時。
- 利用緩存:利用緩存可極大提高系統讀寫速度。
- 消息隊列:消息隊列可以削峯,將攔截大量併發請求,這也是一個異步處理過程,後臺業務根據自己的處理能力,從消息隊列中主動的拉取請求消息進行業務處理。
項目亮點
1. 實現了分佈式Session,分佈式的多臺應用服務器都能響應請求
2. 使用redis做各類緩存,減輕數據庫壓力,提高併發量和訪問速度,秒殺時內存標記減少redis訪問
3. 頁面靜態化,將靜態頁面緩存在瀏覽器,提高頁面加載速度,也減輕服務器壓力
4. 使用RabbitMQ消息隊列異步下單,增強用戶體驗,流量削峯
5. 安全優化: 秒殺接口地址隱藏、數學公式圖形驗證碼(也是削峯重要手段)、 接口防刷限流
- 分佈式Session
使用分佈式緩存存儲Session信息來實現分佈式Session。用戶登錄成功後生成token(SessionId)來標識用戶,寫到Cookie中傳遞給客戶端,在隨後的訪問中Cookie中都包含這個token。
服務端根據傳來的token取得用戶的Session信息。做參數解析後,直接需要用戶信息的方法上傳遞參數。 - Redis做緩存
分佈式Session的用戶信息緩存,頁面緩存,url緩存,對象緩存,驗證碼緩存,秒殺地址緩存,商品庫存緩存,訂單緩存 - 通用緩存Key封裝
使用給key加上前綴的方式區分不同模塊,以前綴+key作爲真正的redis裏的key,使用模板模式對緩存Key做封裝 - 頁面靜態化
將商品詳情頁和訂單詳情頁修改純html的靜態頁面,可緩存在客戶端瀏覽器,客戶端通過ajax請求接口來獲取數據,改爲在客戶端做渲染,減少了和服務端交互的數據,加快頁面加載的速度,減輕了。 - 消息隊列異步下單
- 系統初始化時,把商品庫存數量加載到Redis
- 收到請求,Redis預減庫存,庫存不足,直接返回失敗(後邊的請求對數據庫沒有造成壓力),否則進入3
- 請求入隊RabbitMQ,立即返回排隊中(類似12306正在排隊中)
- 請求出隊,減少庫存, 生成訂單,同時訂單信息寫入Redis
- 客戶端輪詢Redis,是否秒殺成功
- 解決超賣
SQL加庫存數量判斷:防止庫存變成負數
數據庫加唯一索引:防止用戶重複購買 - 保證緩存和數據庫的一致性策略
讀取:先從緩存讀取,讀到數據則直接返回;如果沒有,就讀數據庫,並將數據寫到緩存
更新:先更新數據庫,再刪除緩存
項目完善
- 全局異常處理器不完善,只處理了接口,沒處理頁面
- 靜態資源優化
- 添加秒殺結束標誌,如果用戶在秒殺活動時進入商品詳情頁面,在活動結束後才秒殺,一般情況下秒殺商品數量很少,在秒殺活動期間很快秒殺完,用戶秒殺不成功,但爲了防止秒殺活動不能按時結束,需要在秒殺接口內添加秒殺活動是否結束的判斷,允許後臺手動結束。
項目收穫
應對大併發的思路: 利用緩存,使用異步,應用橫向擴展(分佈式,負載均衡) 配置nginx對應用橫向擴展
編寫優雅的代碼:對輸出結果Result,錯誤代碼CodeMsg,緩存Key的封裝
Ref:https://blog.csdn.net/Brad_PiTt7/article/details/90717429