Java秒杀系统优化(高性能高并发)
源碼免費下載地址:關(guān)注微信公眾號“蝦米聊吧”,回復(fù)關(guān)鍵字“秒殺”
主題:在大并發(fā),大流量的情況下如何提升吞吐量或者說QPS?
而秒殺活動恰恰就是屬于大并發(fā)的情形,因此下面簡單來談?wù)劥蟛l(fā)下秒殺方案的優(yōu)化。
項目采用技術(shù):SpringBoot + MyBatis + MySql + RabbitMq + Redis
RabbitMq安裝參考:https://blog.csdn.net/zwx19921215/article/details/103255834
?
文章首先說明優(yōu)化思路方案和步驟,然后闡述代碼具體實現(xiàn),最后采用JMeter進行簡單壓測。
?
- 針對大并發(fā)的主體優(yōu)化常見有如下幾種方式:
1.頁面緩存 + URL緩存 + 對象級緩存
2.頁面靜態(tài)化(瀏覽器緩存),前后端分離+ajax
3.靜態(tài)資源優(yōu)化(js/css壓縮,減少流量),多個js/css組合,減少連接數(shù)
4.CDN優(yōu)化
?
而作為一個程序猿我們都知道并發(fā)最大的瓶頸基本就是數(shù)據(jù)庫,因此最好就是減少數(shù)據(jù)庫的訪問次數(shù)即加緩存。
?
- 從訪問開始到后端返回整個緩存鏈可以是:
瀏覽器端緩存(頁面靜態(tài)化)--> CDN --> nginx緩存 --> 后端服務(wù)緩存(頁面緩存、對象級緩存等) --> 數(shù)據(jù)庫
?
而作為一名后端工程師的優(yōu)化則主要是針對接口優(yōu)化,這也是我們的重頭戲;
- 接口優(yōu)化大概通過如下步驟處理:
1.redis預(yù)減庫存減少 數(shù)據(jù)庫訪問?
2.內(nèi)存標(biāo)記減少redis訪問?
3.請求先入隊緩沖,異步下單 ,增強用戶體驗
?
- 針對秒殺接口優(yōu)化:
核心思路:減少數(shù)據(jù)庫訪問(數(shù)據(jù)庫瓶頸)
1.系統(tǒng)初始化,把商品庫存數(shù)量加載到redis
2.收到請求,redis預(yù)減庫存,庫存不足,直接返回,否則進入3
3.請求入隊,立即返回 排隊中
4.請求出隊,生成訂單,減少庫存
5.客戶端輪詢,是否秒殺成功
?
- 針對安全方面優(yōu)化:
1.秒殺接口地址隱藏
2.數(shù)學(xué)公式驗證碼
3.接口限流防刷
?
- 秒殺接口地址隱藏:
思路:秒殺開始之前,先去請求接口獲取秒殺地址?
1.接口改造 ,帶上PathVariable參數(shù)
2.添加生成地址的接口
3.秒殺收到請求,先驗證PathVariable
?
- 數(shù)學(xué)公式驗證碼:
目的:防機器人,分散請求
思路:點擊秒殺之前,先輸入驗證碼,分散用戶的請求
1.添加生成驗證碼接口
2.在獲取秒殺路徑的時候,驗證驗證碼
?
- 接口防刷限流:
1.利用redis緩存:比如限制用戶1min中內(nèi)只允許訪問多少次
2.可以利用攔截器減少對業(yè)務(wù)代碼的入侵
?
- 解決超賣問題:
1.數(shù)據(jù)庫加唯一索引:防止用戶重復(fù)購買?
2.更新庫存sql增加庫存數(shù)量判斷:防止庫存變成負(fù)數(shù)?
(update table set count=count-1 where count>0)?
?
- 秒殺優(yōu)化后的主體詳細(xì)流程大致可以分為如下步驟:
5.前端輪詢訂單結(jié)果(是否秒殺成功)
?
- 實現(xiàn)效果如下:
用戶登錄界面
描述商品列表頁:
秒殺商品詳情頁(增加驗證碼):
增加訪問限制(防刷限流):
重復(fù)秒殺處理:
換一個秒殺商品重新秒殺:
秒殺成功進入訂單詳情頁:
數(shù)據(jù)庫查看庫存正常減1
項目結(jié)構(gòu)如下:
秒殺接口部分核心代碼如下:
獲取驗證碼:
@AccessLimit(seconds = 5, maxCount = 5, needLogin = true)@RequestMapping(value = "/path", method = RequestMethod.GET)@ResponseBodypublic 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)@ResponseBodypublic 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);}}執(zhí)行秒殺:
@RequestMapping(value = "/{path}/do_miaosha", method = RequestMethod.POST)@ResponseBodypublic 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);}//驗證pathboolean check = miaoshaService.checkPath(user, goodsId, path);if (!check) {return Result.error(CodeMsg.REQUEST_ILLEGAL);}//內(nèi)存標(biāo)記,減少redis訪問if (localOverMap.size() > 0) {boolean over = localOverMap.get(goodsId);if (over) {return Result.error(CodeMsg.MIAO_SHA_OVER);}}//判斷是否已經(jīng)秒殺到了MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);if (order != null) {return Result.error(CodeMsg.REPEATE_MIAOSHA);}//預(yù)減庫存long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);//10if (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
操作系統(tǒng):centos 虛擬機雙核
測試參數(shù):生成5000個用戶(token信息),設(shè)置5000個線程數(shù)循環(huán)10次,即運行50000次,然后查看聚合報告中的吞吐量。
虛擬機測試結(jié)果吞吐量為2000左右,取決于機器配置
注:該接口是在未加入驗證碼和動態(tài)獲取秒殺路徑的前提下測試的
?
項目完整代碼下載地址:http://zyshare.cn/resource/detail/22
源碼免費下載地址:關(guān)注微信公眾號“蝦米聊吧”,回復(fù)關(guān)鍵字“秒殺”
關(guān)注微信公眾號“蝦米聊吧”,后續(xù)持續(xù)放送“技術(shù)架構(gòu)和資料”干貨!!!
| ? ?一個熱衷于分享技術(shù)和生活的程序猿,讓我們一起交流吧~?????? ? ? ? ? ? ? ???? ? ? ? ? ? ? ? ? ? ? ? 微信掃描二維碼,關(guān)注我的公眾號 |
總結(jié)
以上是生活随笔為你收集整理的Java秒杀系统优化(高性能高并发)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JMeter压测入门简单使用
- 下一篇: 艺卓推出 EIZO ColorEdge