秒杀项目总结及面试常见问题
生活随笔
收集整理的這篇文章主要介紹了
秒杀项目总结及面试常见问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言:
- 這篇文章是慕課網秒殺項目的總結,主講人是Joshua,B站上搜Java秒殺就可以獲取資源,為了防止鏈接過期,這里就不貼鏈接了,大家自己可以去B站搜
- 秒殺項目的思路大致是一樣的,只是有些功能更齊全,核心思想都是解決高并發,所以可能項目不同但是同樣有借鑒作用
- 本文主要對邏輯進行梳理,不包含代碼
- 文末有總結好的常見的面試題
項目亮點
- 使用分布式Session,可以實現讓多臺服務器同時可以響應
- 使用redis做緩存提高訪問速度和并發量,減少數據庫壓力,利用內存標記減少redis的訪問
- 使用頁面靜態化,加快用戶訪問速度,提高QPS,緩存頁面至瀏覽器,前后端分離降低服務器壓力
- 使用消息隊列完成異步下單,提升用戶體驗,削峰和降流
- 安全性優化:雙重md5密碼校驗,秒殺接口地址的隱藏,接口限流防刷,數學公式驗證碼
秒殺流程
一. 環境的搭建
二. 登錄功能的實現
主要內容
- 數據庫設計
- 明文密碼兩次MD5處理
- JSR303參數檢驗+全局異常處理
- 分布式Session
數據庫設計
- 不做注冊,直接登錄,在MySQL中直接創建表
- 用戶表包括id、nickname、password、salt、頭像、注冊時間、上次登錄時間、登錄次數等字段
對登錄密碼進行兩次MD5:細節描述
- 加密的目的:第一次是因為http是明文傳輸的,第二次為了防止數據庫被盜
JSR303參數檢驗
- 通過對輸入的參數LoginVo加注解@validated,然后在傳入的參數mobile和password上加上注解判斷,如@NotNull判斷是否為空,也可以自定義
- 全局異常處理
分布式Session?
三. 實現秒殺功能
- 數據庫設計
- 商品列表頁
- 商品詳情頁
- 訂單詳情頁
- 包括商品表、商品表訂單表、秒殺商品表、秒殺商品訂單表
- 為了展示秒殺商品的詳情需要goods和miaosha_goods中的信息,所以封裝一個GoodsVo,包括價格、庫存、秒殺起始時間
商品詳情頁
訂單詳情頁
- 這里也是秒殺功能的實現
- 在控制層先判斷庫存、然后判斷訂單是否存在、如果都沒有就下單
- 下單順序為減庫存、下定單、寫入秒殺訂單
四. JMeter壓測
- JMeter入門
- 自定義變量模擬多個用戶
- JMeter命令行使用
- Redis壓測工具redis-benchmark
- Spring Boot打war包
- JMeter在windows下是圖形界面
- 打開jmeter.bat運行圖形界面
- 測試計劃中添加線程組
- 在線程組中添加HTTP請求默認值(就是端口號)
- 在線程組中添加HTTP請求(就是要測試的類的URL)、這里可以設置帶參數
- 在線程組中添加監聽器進監聽
- 也可以通過自定義模擬多用戶(寫一個文件,導入即可)
- JMeter在Linux下是命令行進行操
- 在Windows上錄好jmx
- 命令行:sh jmeter.sh -n -t XXX.jmx -l result.jtl
- 把result.jtl導入到jmeter
- 結果是五千并發的情況下,QPS為一千三左右
- redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000
- -c為100個并發連接,-n為100000個請求
- Redis的QPS在十萬左右
五. 頁面優化技術
內容
- 頁面緩存+URL緩存+對象緩存
- 頁面靜態化,前后端分離
- 靜態資源優化
- CDN優化
頁面緩存+URL緩存+對象緩存
- 秒殺的瓶頸在于數據庫,所以要加上各種粒度的緩存,最大的是頁面緩存、最小的是對象緩存
- 頁面緩存步驟(這里指的是商品列表):
- 從redisService中取緩存
- 若緩存中沒有則手動渲染,利用thymeleaf模板
- 然后將頁面加入緩存,并返回渲染頁面
- 不宜時間太長,設置為60s即可
- URL緩存(指的是商品詳情頁)
- 與頁面緩存步驟基本一致,但是需要取緩存和加緩存時要加入參數,GoodsId
- 對象緩存(指的是User對象)
- 前面的頁面緩存和URL緩存適合變化不大的,緩存時間比較短
- 對象緩存是長期緩存,所以需要有個更新的步驟
- 第一步是取緩存
- 若緩存中沒有則去數據庫中查找,并加入緩存;如數據庫中沒有就報錯
- 更新用戶的密碼
- 加緩存之后的QPS大概3000
- 需要先更新數據庫,后刪除緩存;順序不能反,會導致數據不一致:若線程1先刪除緩存,然后線程2讀操作,發現緩存中沒有,把數據庫中的舊數據加入緩存,然后線程1更新數據庫,就會導致緩存與數據庫數據不一致
頁面靜態化,前后端分離
- 頁面靜態化無非就是使用純html頁面+Ajax請求json數據后再填充頁面
- 若A頁面跳轉到B頁面之前需要條件判斷可以先在A頁面中利用ajax請求判斷后再跳轉
- 如果不需要條件判斷可以直接跳轉到B的靜態頁面,讓B自己用ajax請求數據
防超賣
- 發生在減庫存的時候
- 解決方法是在Update語句中加一個判斷
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pBQCw6T3-1596466141006)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/752fa7ac-cacc-4761-90a7-36803e689e22/Untitled.png)]
- 還有一種情況是一個用戶同時發了兩個請求,假如庫存充足,且沒有訂單生成,那么就會減兩次庫存
- 解決辦法是建立用戶和商品的唯一索引
- 做到以上兩點是不會發生超賣的
CDN優化
- CDN是內容分發網絡,相當于緩存,只是部署在全國各地,當用戶發起請求時,會找最近的CDN獲取資源
- **總結:**并發大的瓶頸在于數據庫,所以解決辦法是加各種緩存:從瀏覽器開始,做頁面的靜態化,將靜態頁面緩存在瀏覽器中;請求到達網站之前可以部署一些CDN,讓請求首先訪問CDN;然后是頁面緩存、URL緩存、對象緩存;
- 加緩存的缺點:數據可能不一致,只能做一個平衡
六. 接口優化
內容
- Redis預減庫存減少數據庫訪問
- 內存標記減少Redis訪問
- RabbitMQ隊列緩沖,異步下單,增強用戶體驗
- RabbitMQ安裝與Spring Boot集成
- 訪問Nginx水平擴展
- 壓測
思路:減少數據庫訪問
- 系統初始化,把商品庫存數量加載到Redis中
- 收到請求,Redis預減庫存,庫存不足,直接返回,否則進入3
- 請求入隊,立即返回排隊中
- 請求出隊,生成訂單,減少庫存
- 客戶端輪詢,是否秒殺成功
優化思路
- 系統初始化,把商品庫存數量加載到Redis
- 收到請求,Redis預減庫存,庫存不足,直接返回,否則進入3
- 請求入隊,立即返回排隊中(異步下單)
- 請求出隊,生成訂單,減少庫存,把訂單寫入Redis中
- 客戶端輪詢,判斷是否秒殺成功
秒殺接口優化
- 之前的沒有庫存預熱的步驟是:查庫存-查訂單-修改庫存-生成訂單
- 系統初始化時把庫存加載到數據庫:MiaoshaController 繼承InitializingBean實現afterPropertiesSet方法即可
- 在上一步庫存預熱之后,執行步驟為:查Redis庫存-判斷是否存在訂單-進入隊列-在出隊時才對數據庫進行操作
- 這一步還可以有一個優化,就是內存標記,使用一個Map,將商品ID設置為false,當買空時,設為true;然后每次不是直接訪問Redis進行庫存查詢,而是對商品ID進行條件判斷
- 內存標記的優點是減少對Redis的訪問(當商品已經賣完之后)
七. 安全優化
主要內容
- 秒殺接口地址隱藏
- 數學公式驗證碼
- 接口限流防刷
秒殺接口地址隱藏
- 雖然前端頁面在秒殺未開始時秒殺按鈕設置為不可用,但是有可能用戶通過前端js代碼找到秒殺地址在秒殺未開始時直接訪問,秒殺接口隱藏的目的是用戶通過js獲取到的秒殺地址并不能讓其完成秒殺功能
- 在秒殺之前要先通過Controller中的/path路徑下的類隨機生成一個path,然后和用戶ID一起存入Redis,在執行秒殺的時候再從Redis中取Path進行驗證,然后進行秒殺
數學公式驗證碼
- 作用:接口防刷;錯開請求
- 在獲取Path是進行驗證
接口限流防刷
- 當一個用戶訪問接口時,把訪問次數寫入緩存,并設置有效期
- 一分鐘之內如果用戶訪問,則緩存中的訪問次數加一,如果次數超限進行限流操作
- 如果一分鐘內沒有超限,緩存中數據消失,下次再訪問時重新寫入緩存
使用一個通用攔截器
- 首先寫一個注解AccessLimit
- 后面每個類只需要加注解即可設置防刷次數
- 定義攔截器:繼承HandlerInterceptorAdapter類
八. 總結
1.問題總結(主要從三個方面:項目本身的問題、可能出現的問題、可改進的地方)
1.1 項目本身的問題
- 畫一下項目的架構圖
- 講一下秒殺流程
- 秒殺模塊怎么設計的
- 秒殺部分是怎么做的
- 分布式Session是怎么實現的
- 如何解決超賣?mysql鎖
- 如何解決重復下單?mysql唯一索引
- 如何防刷?驗證碼+通用攔截器限流
- 消息隊列的作用?異步削峰
- 壓測沒有?用什么壓測?QPS是多少?
- 庫存預減用的是哪個redis方法
1.2 可能出現的問題
- 緩存和數據庫數據一致性如何保證?
- 如果項目中的redis服務掛掉,如何減輕數據庫的壓力
- 假如減了庫存但用戶沒有支付,怎么將庫存還原繼續進行搶購
1.3 可改進的地方
- 系統瓶頸在哪?如何查找,如何再優化?
- 除了你項目里面的優化,你還有什么優化策略嗎?(同上一個問題)
- 使用了大量緩存,那么就存在緩存擊穿和緩存雪崩以及緩存一致性等問題
- 大量的使用緩存,對于緩存服務器也有很大的壓力,如何減少redis的訪問
- 在高并發請求的業務場景,大量請求來不及處理,甚至出現請求堆積的情況
- 怎么保證一個用戶不能重復下單
- 怎么解決超賣現象
- 頁面靜態化的過程
常見問題的回答
總結
以上是生活随笔為你收集整理的秒杀项目总结及面试常见问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序人生丨程序员必会的 10 种核心技能
- 下一篇: Logistic 回归与 Softmax