前言
書接上文,上面的兩個章節後我們都知道秒殺的一個業務處理該如何去優化,
java代碼優化方案3(redis預減庫存,redis標記商品)
java代碼優化方案4(Rabbitmq異步下單)
當然也僅僅就是後臺對性能的一個優化,本次的話就是結合後臺redis緩存的機制和前臺多次請求來隱藏自己的接口。
怎麼叫多次請求呢?其實就是你操作起來可能就一次請求,可實際上。。。
1、發送請求到後臺動態獲取一個地址(並redis保存),前臺接收地址(path)。
2、在第一次請求成功後會調用第二次請求,第二次請求(我們上兩章的內容)攜帶了path作爲我們的路徑(redis判斷),等處理業務過後,雖然我們會知道後臺是否處理成功,但是我們無法知道秒殺是否成功!!(Rabbitmq的原因)
3、第二次請求成功響應過後發送最後第三次請求,用來判斷這個用戶的這個商品是否成功秒殺
所以我會從三次請求來講這個藉口地址隱藏。
第一次請求
getMiaoshaPath
function getMiaoshaPath(){
var goodsId = $("#goodsId").val();
g_showLoading();
$.ajax({
url:"/miaosha/path",
type:"GET",
data:{
goodsId:goodsId,
verifyCode:$("#verifyCode").val()
},
success:function(data){
if(data.code == 0){
var path = data.data;
doMiaosha(path);
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("客戶端請求有誤");
}
});
}
getMiaoshaPath
/**第二階段
* 生成秒殺地址,把秒殺的時間數字傳過來
* @param request
* @param user
* @param goodsId
* @param verifyCode
* @return
*/
@RequestMapping(value="/path", method=RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaPath(HttpServletRequest request, HttpServletResponse response, MiaoshaUser user,
@RequestParam("goodsId")long goodsId,
@RequestParam(value="verifyCode", defaultValue="0")int verifyCode
) {
if(user == null) {//判斷用戶是否有cookie
return Result.error(CodeMsg.SESSION_ERROR);
}
boolean check = miaoshaService.checkVerifyCode(user, goodsId, verifyCode);//(vc)判斷這個驗證碼是否正確,獲取redis中的數據
if(!check) {
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
String path =miaoshaService.createMiaoshaPath(user, goodsId);//(mp) 生成一個地址,並存儲在redis中
return Result.success(path);
}
前臺請求:
後臺:
第二次請求
doMiaosha
function doMiaosha(path){
$.ajax({
url:"/miaosha/"+path+"/do_miaosha",
type:"POST",
data:{
goodsId:$("#goodsId").val()
},
success:function(data){
if(data.code == 0){
//window.location.href="/order_detail.htm?orderId="+data.data.id;
getMiaoshaResult($("#goodsId").val());
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("客戶端請求有誤");
}
});
}
miaosha
/**第二階段
* QPS:1306
* 5000 * 10
* QPS: 2114
*
* 隱藏原有的秒殺地址
* */
@RequestMapping(value="/{path}/do_miaosha", method=RequestMethod.POST)
@ResponseBody
public Result<Integer> miaosha(HttpServletRequest request, HttpServletResponse response,
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);
}
//1、驗證path
boolean check = miaoshaService.checkPath(user, goodsId, path);//(mp)判斷傳過來的地址,獲取redis中的數據
if(!check){
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
//2、內存標記,減少redis訪問
boolean over = localOverMap.get(goodsId);
if(over) {
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
//3、預減庫存
long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, ""+goodsId);//10
if(stock < 0) {
localOverMap.put(goodsId, true);
redisService.set(MiaoshaKey.isGoodsOver, ""+goodsId, true);//商品庫存設置爲空
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
//4、判斷是否已經秒殺到了
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order != null) {
return Result.error(CodeMsg.REPEATE_MIAOSHA);
}
//入隊
MiaoshaMessage mm = new MiaoshaMessage();
mm.setUser(user);
mm.setGoodsId(goodsId);
sender.sendMiaoshaMessage(mm);
return Result.success(0);//排隊中
}
前臺:
後臺:
重點:
其實按照我們以前的套路來說,其實在這個請求就已經結束了,因爲以前我們的基本業務都在這個請求裏面,
可是就是因爲我們使用了rabbitmq去異步處理下單的這個業務,所以我們在這個請求的返回結果中是無法直接的獲取到秒殺是否成功!!
但是異步處理就是說這個下單的業務處理就完全從那個線程去除掉了。那提高的性能不言而喻。
可是至於另外線程完成的事,我們並不瞭解,只能通過某個標識來確定是否秒殺過。
第三次請求
getMiaoshaResult
function getMiaoshaResult(goodsId){
g_showLoading();
$.ajax({
url:"/miaosha/result",
type:"GET",
data:{
goodsId:$("#goodsId").val(),
},
success:function(data){
if(data.code == 0){
var result = data.data;
if(result < 0){
layer.msg("對不起,秒殺失敗");
}else if(result == 0){//繼續輪詢
setTimeout(function(){
getMiaoshaResult(goodsId);
}, 200);
}else{
layer.confirm("恭喜你,秒殺成功!查看訂單?", {btn:["確定","取消"]},
function(){
window.location.href="/order_detail.htm?orderId="+result;
},
function(){
layer.closeAll();
});
}
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("客戶端請求有誤");
}
});
}
miaoshaResult
/**第二階段
* orderId:成功
* -1:秒殺失敗
* 0: 排隊中
* */
@RequestMapping(value="/result", method=RequestMethod.GET)
@ResponseBody
public Result<Long> miaoshaResult(HttpServletRequest request, HttpServletResponse response,Model model,MiaoshaUser user,
@RequestParam("goodsId")long goodsId) {
model.addAttribute("user", user);
if(user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
long result = miaoshaService.getMiaoshaResult(user.getId(), goodsId);//(moug) 判斷是否已經秒殺
return Result.success(result);
}
前臺
是否存在的一個判斷
後臺:
後言
重點是要了解這個藉口地址是如何獲取的,
其次是要判斷的一個獲取是否還有庫存
項目代碼:
鏈接:https://pan.baidu.com/s/1lLuT_BdfpdYxuKSGe_TQqw
提取碼:iqeb