源碼免費下載地址:關注微信公衆號“蝦米聊吧”,回覆關鍵字“秒殺”
主題:在大併發,大流量的情況下如何提升吞吐量或者說QPS?
而秒殺活動恰恰就是屬於大併發的情形,因此下面簡單來談談大併發下秒殺方案的優化。
項目採用技術:SpringBoot + MyBatis + MySql + RabbitMq + Redis
RabbitMq安裝參考:https://blog.csdn.net/zwx19921215/article/details/103255834
文章首先說明優化思路方案和步驟,然後闡述代碼具體實現,最後採用JMeter進行簡單壓測。
- 針對大併發的主體優化常見有如下幾種方式:
1.頁面緩存 + URL緩存 + 對象級緩存
2.頁面靜態化(瀏覽器緩存),前後端分離+ajax
3.靜態資源優化(js/css壓縮,減少流量),多個js/css組合,減少連接數
4.CDN優化
而作爲一個程序猿我們都知道併發最大的瓶頸基本就是數據庫,因此最好就是減少數據庫的訪問次數即加緩存。
- 從訪問開始到後端返回整個緩存鏈可以是:
瀏覽器端緩存(頁面靜態化)--> CDN --> nginx緩存 --> 後端服務緩存(頁面緩存、對象級緩存等) --> 數據庫
而作爲一名後端工程師的優化則主要是針對接口優化,這也是我們的重頭戲;
- 接口優化大概通過如下步驟處理:
1.redis預減庫存減少 數據庫訪問
2.內存標記減少redis訪問
3.請求先入隊緩衝,異步下單 ,增強用戶體驗
- 針對秒殺接口優化:
核心思路:減少數據庫訪問(數據庫瓶頸)
1.系統初始化,把商品庫存數量加載到redis
2.收到請求,redis預減庫存,庫存不足,直接返回,否則進入3
3.請求入隊,立即返回 排隊中
4.請求出隊,生成訂單,減少庫存
5.客戶端輪詢,是否秒殺成功
- 針對安全方面優化:
1.秒殺接口地址隱藏
2.數學公式驗證碼
3.接口限流防刷
- 秒殺接口地址隱藏:
思路:秒殺開始之前,先去請求接口獲取秒殺地址
1.接口改造 ,帶上PathVariable參數
2.添加生成地址的接口
3.秒殺收到請求,先驗證PathVariable
- 數學公式驗證碼:
目的:防機器人,分散請求
思路:點擊秒殺之前,先輸入驗證碼,分散用戶的請求
1.添加生成驗證碼接口
2.在獲取秒殺路徑的時候,驗證驗證碼
- 接口防刷限流:
1.利用redis緩存:比如限制用戶1min中內只允許訪問多少次
2.可以利用攔截器減少對業務代碼的入侵
- 解決超賣問題:
1.數據庫加唯一索引:防止用戶重複購買
2.更新庫存sql增加庫存數量判斷:防止庫存變成負數
(update table set count=count-1 where count>0)
- 秒殺優化後的主體詳細流程大致可以分爲如下步驟:
- 程序啓動後將秒殺庫存寫入redis,設置內存標記商品是否已秒殺完
- 加入驗證碼
- 秒殺前首先獲取秒殺路徑
- 開始秒殺:
a.判斷用戶登錄信息是否異常
b.驗證秒殺路徑
c.獲取秒殺商品內存標記並判斷是否已秒殺完
d.redis獲取用戶訂單,判斷該用戶是否是重複秒殺
e.如果是正常秒殺,對於未秒殺完的商品進行redis減庫存操作
f.如果redis庫存已<0,標記內存商品已秒殺完
g.秒殺正常則加入隊列,異步處理訂單入庫,返回排隊中
5.前端輪詢訂單結果(是否秒殺成功)
- 實現效果如下:
用戶登錄界面
描述商品列表頁:
秒殺商品詳情頁(增加驗證碼):
增加訪問限制(防刷限流):
重複秒殺處理:
換一個秒殺商品重新秒殺:
秒殺成功進入訂單詳情頁:
數據庫查看庫存正常減1
項目結構如下:
秒殺接口部分核心代碼如下:
獲取驗證碼:
@AccessLimit(seconds = 5, maxCount = 5, needLogin = true)
@RequestMapping(value = "/path", method = RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaPath(HttpServletRequest request, MiaoshaUser user,
@RequestParam("goodsId") long goodsId,
@RequestParam(value = "verifyCode", defaultValue = "0") int verifyCode
) {
if (user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
boolean check = miaoshaService.checkVerifyCode(user, goodsId, verifyCode);
if (!check) {
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
String path = miaoshaService.createMiaoshaPath(user, goodsId);
return Result.success(path);
}
獲取秒殺路徑:
@RequestMapping(value = "/verifyCode", method = RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaVerifyCod(HttpServletResponse response, MiaoshaUser user,
@RequestParam("goodsId") long goodsId) {
if (user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
try {
BufferedImage image = miaoshaService.createVerifyCode(user, goodsId);
OutputStream out = response.getOutputStream();
ImageIO.write(image, "JPEG", out);
out.flush();
out.close();
return null;
} catch (Exception e) {
e.printStackTrace();
return Result.error(CodeMsg.MIAOSHA_FAIL);
}
}
執行秒殺:
@RequestMapping(value = "/{path}/do_miaosha", method = RequestMethod.POST)
@ResponseBody
public Result<Integer> miaosha(Model model, MiaoshaUser user,
@RequestParam("goodsId") long goodsId,
@PathVariable("path") String path) {
model.addAttribute("user", user);
if (user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
//驗證path
boolean check = miaoshaService.checkPath(user, goodsId, path);
if (!check) {
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
//內存標記,減少redis訪問
if (localOverMap.size() > 0) {
boolean over = localOverMap.get(goodsId);
if (over) {
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
}
//判斷是否已經秒殺到了
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if (order != null) {
return Result.error(CodeMsg.REPEATE_MIAOSHA);
}
//預減庫存
long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);//10
if (stock < 0) {
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
//mq入隊
MiaoshaMessage mm = new MiaoshaMessage();
mm.setUser(user);
mm.setGoodsId(goodsId);
sender.sendMiaoshaMessage(mm);
return Result.success(0);//排隊中
- 採用JMeter做個簡單的壓力測試
JMeter簡單使用參考:https://blog.csdn.net/zwx19921215/article/details/103261289
操作系統:centos 虛擬機雙核
測試參數:生成5000個用戶(token信息),設置5000個線程數循環10次,即運行50000次,然後查看聚合報告中的吞吐量。
虛擬機測試結果吞吐量爲2000左右,取決於機器配置
注:該接口是在未加入驗證碼和動態獲取秒殺路徑的前提下測試的
項目完整代碼下載地址:http://zyshare.cn/resource/detail/22
源碼免費下載地址:關注微信公衆號“蝦米聊吧”,回覆關鍵字“秒殺”
關注微信公衆號“蝦米聊吧”,後續持續放送“技術架構和資料”乾貨!!!
一個熱衷於分享技術和生活的程序猿,讓我們一起交流吧~
微信掃描二維碼,關注我的公衆號 |