改進的點
- 之前的秒殺下單接口被腳本不停的刷
- 秒殺驗證邏輯和秒殺下單接口強關聯,代碼冗餘度高
- 秒殺驗證邏輯複雜,對交易系統產生無關聯複雜
秒殺令牌
//申城秒殺令牌
//校驗用戶以及商品
@Override
public String generateSecondKillToken(Integer promoId,Integer itemId,Integer userId) {
//獲取對應商品的秒殺活動信息
PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
//dataobject->model
PromoModel promoModel = convertFromDataObject(promoDO);
if(promoModel == null){
return null;
}
//判斷當前時間是否秒殺活動即將開始或正在進行,若不是無法生成令牌
if(promoModel.getStartDate().isAfterNow()){
promoModel.setStatus(1);
}else if(promoModel.getEndDate().isBeforeNow()){
promoModel.setStatus(3);
}else{
promoModel.setStatus(2);
}
//
//使用緩存校驗itemModel
ItemModel itemModel = itemService.getItemByIdInCache(itemId);
if(itemModel == null){
return null;
}
//判斷用戶是否存在
UserModel userModel = userService.getUserByIdInCache(userId)
if(userModel == null){
return null;
}
//生成tockey存入redis內,並給一個5分鐘的有效期
if(promoModel.getStatus().intValue()!=2){
return null;
}else{
//獲取秒殺大閘的count數量
long result = redisTemplate.opsForValue().increment("promo_door_count_" + promoId,-1);
if(result<0) return null;
//生成一個秒殺令牌
String token = UUID.randomUUID().toString().replace("-","");
redisTemplate.opsForValue().set("promo_token_"+promoId+"_userid_"+userId+"_itemid_"+itemId,token);
redisTemplate.expire("promo_token_"+promoId+"_userid_"+userId+"_itemid_"+itemId,5, TimeUnit.MINUTES);
return token;
}
}
秒殺大閘
//限制秒殺令牌的數量:多少個用戶可以獲取到秒殺令牌
//將秒殺大閘限制到redis內
redisTemplate.opsForValue().set("promo_door_count_"+promoId,itemModel.getStock().intValue()*4);
隊列泄洪(擁塞窗口)
在orderController中有用戶下單時,使用線程池,從線程池中選取線程進行操作,每次
最多有20個用戶
可以進行下單的操作
//同步調用線程池的submit方法
//同步調用線程池的subnmit方法
//擁塞窗口爲20的等待隊列,用來隊列化泄洪
Future<Object> future = executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
//加入庫存流水init狀態
String stockLogId = itemService.initStockLog(itemId,amount);
if(!mqProducer.transactionAsyncReduceStock(userModel.getId(),promoId,itemId,amount,stockLogId)){
throw new BusinessException(EmBusinessError.UNKNOWN_ERROR,"下單失敗");
}
return null;
}
});