方案实现流程:
提前对应的商品库存生成好对应令牌(100个令牌)——>也就是令牌桶,在10万个请求中,只要谁能够获取到令牌谁就能够秒杀成功, 获取到秒杀令牌后,在使用mq异步实现修改减去库
原理图:
注意:公司级秒杀服务中,秒杀生产者和消费者要分别放在不同的服务,避免生产者挂了消费者也连着挂了
(1)MQ相关配置:RabbitmqConfig
@Component
public class RabbitmqConfig {
// 添加修改库存队列
public static final String MODIFY_INVENTORY_QUEUE = "modify_inventory_queue";
// 交换机名称
private static final String MODIFY_EXCHANGE_NAME = "modify_exchange_name";
// 1.添加交换机队列
@Bean
public Queue directModifyInventoryQueue() {
return new Queue(MODIFY_INVENTORY_QUEUE);
}
// 2.定义交换机
@Bean
DirectExchange directModifyExchange() {
return new DirectExchange(MODIFY_EXCHANGE_NAME);
}
// 3.修改库存队列绑定交换机
@Bean
Binding bindingExchangeintegralDicQueue() {
return BindingBuilder.bind(directModifyInventoryQueue()).to(directModifyExchange()).with("modifyRoutingKey");
}
}
(2)生产者发送消息:SpikeCommodityProducer
@Component
@Slf4j
public class SpikeCommodityProducer implements RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@Transactional
public void send(JSONObject jsonObject) {
String jsonString = jsonObject.toJSONString();
System.out.println("jsonString:" + jsonString);
String messAgeId = UUID.randomUUID().toString().replace("-", "");
// 封装消息
Message message = MessageBuilder.withBody(jsonString.getBytes())
.setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding("utf-8").setMessageId(messAgeId)
.build();
// 构建回调返回的数据(消息id)
this.rabbitTemplate.setMandatory(true);
this.rabbitTemplate.setConfirmCallback(this);
CorrelationData correlationData = new CorrelationData(jsonString);
rabbitTemplate.convertAndSend("modify_exchange_name", "modifyRoutingKey", message, correlationData);
}
// 生产消息确认机制 生产者往服务器端发送消息的时候,采用应答机制
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String jsonString = correlationData.getId();
System.out.println("消息id:" + correlationData.getId());
if (ack) {
log.info(">>>使用MQ消息确认机制确保消息一定要投递到MQ中成功");
return;
}
JSONObject jsonObject = JSONObject.parseObject(jsonString);
// 生产者消息投递失败的话,采用递归重试机制
send(jsonObject);
log.info(">>>使用MQ消息确认机制投递到MQ中失败");
}
}
StockConsumer :
@Component
@Slf4j
public class StockConsumer {
@Autowired
private SeckillMapper seckillMapper;
@Autowired
private OrderMapper orderMapper;
@RabbitListener(queues = "modify_inventory_queue")
@Transactional
public void process(Message message, @Headers Map headers, Channel channel) throws IOException {
String messageId = message.getMessageProperties().getMessageId();
String msg = new String(message.getBody(), "UTF-8");
log.info(">>>messageId:{},msg:{}", messageId, msg);
JSONObject jsonObject = JSONObject.parseObject(msg);
// 1.获取秒杀id
Long seckillId = jsonObject.getLong("seckillId");
SeckillEntity seckillEntity = seckillMapper.findBySeckillId(seckillId);
if (seckillEntity == null) {
log.warn("seckillId:{},商品信息不存在!", seckillId);
return;
}
Long version = seckillEntity.getVersion();
int inventoryDeduction = seckillMapper.inventoryDeduction(seckillId, version);
if (!toDaoResult(inventoryDeduction)) {
log.info(">>>seckillId:{}修改库存失败>>>>inventoryDeduction返回为{} 秒杀失败!", seckillId, inventoryDeduction);
return;
}
// 2.添加秒杀订单
OrderEntity orderEntity = new OrderEntity();
String phone = jsonObject.getString("phone");
orderEntity.setUserPhone(phone);
orderEntity.setSeckillId(seckillId);
orderEntity.setState(1l);
int insertOrder = orderMapper.insertOrder(orderEntity);
if (!toDaoResult(insertOrder)) {
return;
}
log.info(">>>修改库存成功seckillId:{}>>>>inventoryDeduction返回为{} 秒杀成功", seckillId, inventoryDeduction);
}
// 调用数据库层判断
public Boolean toDaoResult(int result) {
return result > 0 ? true : false;
}
}
(1)OrderSeckillService :
public interface OrderSeckillService {
@RequestMapping("/getOrder")
public BaseResponse getOrder(String phone, Long seckillId);
}
(2)OrderSeckillServiceImpl
@RestController
public class OrderSeckillServiceImpl extends BaseApiService implements OrderSeckillService {
@Autowired
private OrderMapper orderMapper;
@Override
public BaseResponse getOrder(String phone, Long seckillId) {
if (StringUtils.isEmpty(phone)) {
return setResultError("手机号码不能为空!");
}
if (seckillId == null) {
return setResultError("商品库存id不能为空!");
}
OrderEntity orderEntity = orderMapper.findByOrder(phone, seckillId);
if (orderEntity == null) {
return setResultError("正在排队中.....");
}
return setResultSuccess("恭喜你秒杀成功!");
}
}
上一篇:乐观锁实现防止库存超卖
下一篇: 责任链模式实现网关限流
若对你有帮助,欢迎关注!!点赞!!评论!!