秒杀优化

pull/8/head
czk 2024-05-02 19:29:55 +08:00
parent 7c67d8de5b
commit 387c5e3db3
16 changed files with 130 additions and 100 deletions

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mall.common.domain.OrderItemEntity; import com.mall.common.domain.OrderItemEntity;
import com.mall.server.config.DelayConfig; import com.mall.server.config.DelayConfig;
import com.mall.server.config.MqConfig;
import com.mall.server.constant.SpikesConstant; import com.mall.server.constant.SpikesConstant;
import com.mall.server.service.OrderItemService; import com.mall.server.service.OrderItemService;
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Channel;
@ -48,7 +49,39 @@ public class SpikesMonitor {
try { try {
channel.basicAck(deliveryTag,false); channel.basicAck(deliveryTag,false);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); try {
if(deliveryTag<3){
Thread.sleep(3000);
channel.basicNack(deliveryTag,false,true);
}else {
channel.basicNack(deliveryTag,false,false);
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
@RabbitListener(queues = MqConfig.DXQUEUE)
public void orderConsumer(String meg, Message message, Channel channel){
log.info("下订单"+meg);
long deliveryTag = message.getMessageProperties().getDeliveryTag();
orderItemService.save(JSON.parseObject(meg,OrderItemEntity.class));
try {
channel.basicAck(deliveryTag,false);
} catch (IOException e) {
try {
if(deliveryTag<3){
Thread.sleep(3000);
channel.basicNack(deliveryTag,false,true);
}else {
channel.basicNack(deliveryTag,false,false);
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
} }
} }

View File

@ -1,6 +1,5 @@
package com.mall.server.service.impl; package com.mall.server.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mall.common.constant.TokenConstants; import com.mall.common.constant.TokenConstants;
@ -14,6 +13,7 @@ import com.mall.common.result.Result;
import com.mall.common.utils.IdUtils; import com.mall.common.utils.IdUtils;
import com.mall.common.utils.StringUtils; import com.mall.common.utils.StringUtils;
import com.mall.server.config.DelayConfig; import com.mall.server.config.DelayConfig;
import com.mall.server.config.MqConfig;
import com.mall.server.constant.SpikesConstant; import com.mall.server.constant.SpikesConstant;
import com.mall.server.domain.SkuEntity; import com.mall.server.domain.SkuEntity;
import com.mall.server.domain.SpikesEntity; import com.mall.server.domain.SpikesEntity;
@ -21,12 +21,10 @@ import com.mall.server.mapper.SpikesMapper;
import com.mall.server.service.OrderItemService; import com.mall.server.service.OrderItemService;
import com.mall.server.service.SkuService; import com.mall.server.service.SkuService;
import com.mall.server.service.SpikesService; import com.mall.server.service.SpikesService;
import com.mall.server.service.SpuService;
import org.redisson.api.RSemaphore; import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -39,7 +37,7 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
*serviceImpl * serviceImpl
*/ */
@Service @Service
public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity> public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
@ -69,14 +67,15 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
/** /**
* *
*
* @param spikesRequest * @param spikesRequest
* @return * @return
*/ */
@Transactional @Transactional
@Override @Override
public Result add(SpikesRequest spikesRequest) { public Result add(SpikesRequest spikesRequest) {
if(spikesRequest.getCreateTime().getMinutes()%60!=0){ if (spikesRequest.getCreateTime().getMinutes() % 60 != 0) {
throw new BizException(500,"不是整点"); throw new BizException(500, "不是整点");
} }
//一天后的0点 //一天后的0点
Date date = Date.from( Date date = Date.from(
@ -84,8 +83,8 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
.withSecond(0).withNano(0) .withSecond(0).withNano(0)
.plusDays(1).atZone(ZoneId.systemDefault()).toInstant() .plusDays(1).atZone(ZoneId.systemDefault()).toInstant()
); );
if(spikesRequest.getCreateTime().compareTo(date)<0){ if (spikesRequest.getCreateTime().compareTo(date) < 0) {
throw new BizException(500,"时间必须是第二天0点"); throw new BizException(500, "时间必须是第二天0点");
} }
//秒杀表信息 //秒杀表信息
int spikesId = spikesMapper.insert( int spikesId = spikesMapper.insert(
@ -103,7 +102,7 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
); );
//修改sku商品信息 //修改sku商品信息
skuService.updateBatchById( skuService.updateBatchById(
spikesRequest.getSkuList().stream().map(c-> spikesRequest.getSkuList().stream().map(c ->
SkuEntity.builder() SkuEntity.builder()
.id(c.getSkuId()) .id(c.getSkuId())
.inventoryRestrict(c.getInventoryRestrict()) .inventoryRestrict(c.getInventoryRestrict())
@ -137,26 +136,27 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
.endTime(spikesRequest.getEndTime()).build() .endTime(spikesRequest.getEndTime()).build()
).collect(Collectors.toList()); ).collect(Collectors.toList());
//后期存入redis放入查询列表中 //后期存入redis放入查询列表中
activitySkuVoList.forEach(c->{ activitySkuVoList.forEach(c -> {
//redis秒杀信息存在时间 //redis秒杀信息存在时间
long expire = (c.getEndTime().getTime() - System.currentTimeMillis()) / 60000; long expire = (c.getEndTime().getTime() - System.currentTimeMillis()) / 60000;
redisCache.setCacheObject(SpikesConstant.SPIKES_SKUID +c.getId(),c,expire, TimeUnit.MINUTES); redisCache.setCacheObject(SpikesConstant.SPIKES_SKUID + c.getId(), c, expire, TimeUnit.MINUTES);
}); });
return Result.success(true,"添加秒杀成功"); return Result.success(true, "添加秒杀成功");
} }
/** /**
* *
*
* @param skuId * @param skuId
* @return * @return
*/ */
@Override @Override
public Result spike(Long skuId) { public Result spike(Long skuId) {
UserInfo login = getLogin(); UserInfo login = getLogin();
if(redisCache.hasKey("spike_"+login.getId()+skuId)){ if (redisCache.hasKey("spike_" + login.getId() + skuId)) {
throw new BizException(500,"禁止重复抢购欧"); throw new BizException(500, "禁止重复抢购欧");
} }
redisCache.setCacheObject("spike_"+login.getId()+skuId,IdUtils.genId().toString(),1L,TimeUnit.MINUTES); redisCache.setCacheObject("spike_" + login.getId() + skuId, IdUtils.genId().toString(), 1L, TimeUnit.MINUTES);
SkuEntity skuEntity = skuService.getOne( SkuEntity skuEntity = skuService.getOne(
new LambdaQueryWrapper<SkuEntity>() new LambdaQueryWrapper<SkuEntity>()
.eq(skuId != null, SkuEntity::getId, skuId) .eq(skuId != null, SkuEntity::getId, skuId)
@ -165,16 +165,15 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
new LambdaQueryWrapper<SpikesEntity>() new LambdaQueryWrapper<SpikesEntity>()
.eq(skuEntity.getSpikesId() != null, SpikesEntity::getSpikesId, skuEntity.getSpikesId()) .eq(skuEntity.getSpikesId() != null, SpikesEntity::getSpikesId, skuEntity.getSpikesId())
); );
if(spikesEntity.getCreateTime().compareTo(new Date())>0 || spikesEntity.getEndTime().compareTo(new Date())<0){ if (spikesEntity.getCreateTime().compareTo(new Date()) > 0 || spikesEntity.getEndTime().compareTo(new Date()) < 0) {
throw new BizException(500,skuEntity.getName()+"该商品不在抢购时间内"); throw new BizException(500, skuEntity.getName() + "该商品不在抢购时间内");
} }
RSemaphore semaphore = redissonClient.getSemaphore(SpikesConstant.SPIKES_INVENTORY + skuId); RSemaphore semaphore = redissonClient.getSemaphore(SpikesConstant.SPIKES_INVENTORY + skuId);
if(!semaphore.tryAcquire(1)){ if (!semaphore.tryAcquire(1)) {
throw new BizException(500,"系统繁忙,商品已抢购完"); throw new BizException(500, "系统繁忙,商品已抢购完");
} }
String orderSn = IdUtils.genId(); String orderSn = IdUtils.genId();
orderItemService.save( OrderItemEntity orderItem = OrderItemEntity.builder()
OrderItemEntity.builder()
.userId(login.getId()) .userId(login.getId())
.orderSn(orderSn) .orderSn(orderSn)
.skuId(skuId) .skuId(skuId)
@ -185,22 +184,28 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
// .couponAmount() // .couponAmount()
// .integrationAmount() // .integrationAmount()
.realAmount(skuEntity.getActivityPrice()) .realAmount(skuEntity.getActivityPrice())
.status(0).build() .status(0).build();
); //异步下订单
rabbitTemplate.convertAndSend(MqConfig.DXEXCHANGE, MqConfig.ROUTINGKEY, orderItem.toString(),
message -> {
message.getMessageProperties().setMessageId(IdUtils.genId());
return message;
});
rabbitTemplate.convertAndSend( rabbitTemplate.convertAndSend(
DelayConfig.EXCHANGE, DelayConfig.ROUKEYCZK, skuId + "," +orderSn , DelayConfig.EXCHANGE, DelayConfig.ROUKEYCZK, skuId + "," + orderSn,
message -> { message -> {
message.getMessageProperties().setMessageId(IdUtils.genId()); message.getMessageProperties().setMessageId(IdUtils.genId());
return message; return message;
} }
); );
return Result.success(skuId,"下单成功"); return Result.success(skuId, "下单成功");
} }
public UserInfo getLogin(){ public UserInfo getLogin() {
String token = request.getHeader(TokenConstants.TOKEN); String token = request.getHeader(TokenConstants.TOKEN);
if (StringUtils.isBlank(token)){ if (StringUtils.isBlank(token)) {
throw new BizException(401,"未登录"); throw new BizException(401, "未登录");
} }
UserInfo userInfo = redisCache.getCacheObject(TokenConstants.TOKEN + token); UserInfo userInfo = redisCache.getCacheObject(TokenConstants.TOKEN + token);
return userInfo; return userInfo;