RabbitMQ在秒杀场景中的简单应用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sijg16/article/details/79144406

 秒杀业务的核心是库存处理,用户购买成功后会进行减库存操作,并记录购买明细。当秒杀开始时,大量用户同时发起请求,这是一个并行操作,多条更新库存数量的SQL语句会同时竞争秒杀商品所处数据库表里的那行数据,导致库存的减少数量与购买明细的增加数量不一致,因此,我们使用RabbitMQ进行削峰限流并且将请求数据串行处理。

 首先我先设计了两张表,一张是秒杀库存表,另一张是秒杀成功表。

 CREATE TABLE seckill

 (

seckill_id           BIGINT NOT NULL AUTO_INCREMENT COMMENT '商品库存id',
    NAME                 VARCHAR(120) NOT NULL COMMENT '商品名称',
    number               INT NOT NULL COMMENT '库存数量',
    initial_price        BIGINT NOT NULL COMMENT '原价',
    seckill_price        BIGINT NOT NULL COMMENT '秒杀价',
    sell_point           VARCHAR(500) NOT NULL COMMENT '卖点',
    create_time          TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '秒杀创建时间',
    start_time           TIMESTAMP NOT NULL COMMENT '秒杀开始时间',
    end_time             TIMESTAMP NOT NULL COMMENT '秒杀结束时间',
    PRIMARY KEY (seckill_id)
 );
 ALTER TABLE seckill COMMENT '秒杀库存表';
 CREATE INDEX idx_create_time ON seckill
 (
    create_time
 );
 CREATE INDEX idx_start_time ON seckill
 (
    start_time
 );
 CREATE INDEX idx_end_time ON seckill
 (
    end_time
 );

 CREATE TABLE success_killed
 (
    success_id           BIGINT NOT NULL AUTO_INCREMENT COMMENT '秒杀成功id',
    seckill_id           BIGINT NOT NULL COMMENT '秒杀商品id',
    user_phone           BIGINT NOT NULL COMMENT '用户手机号',
    state                TINYINT NOT NULL DEFAULT -1 COMMENT '状态标志:-1:无效;0:成功',
    create_time          TIMESTAMP NOT NULL COMMENT '秒杀成功创建时间',
    PRIMARY KEY (success_id)
 );
 ALTER TABLE success_killed COMMENT '秒杀成功表';
 CREATE INDEX idx_create_time ON success_killed
 (
    create_time
 );

接下来我开始模拟用户请求,往RabbitMQ中发送100个手机号。

public String goods(@PathVariable("seckillId")Long seckillId){

    for(int i = 100;i<200;i++){

        seckillService.setGoods(seckillId,"13145678"+i);

    }

    return "success";

}

public void setGoods(Long seckillId,StringuserPhone){

       String goods = seckillId+"/"+userPhone;  

       rabbitTemplate.convertAndSend("executeSeckill",goods);

}

然后我用RabbitMQ监听seckill_queue队列,当队列中接收到消息就会自动触发RabbitMQService类中的executeSeckill方法,消息将作为方法的参数传递进来执行秒杀操作。

public class RabbitMQService {

    @Autowired

    privateSeckillMapper seckillMapper;

    @Autowired

    privateSuccessKilledMapper successKilledMapper;

    privatestatic final Logger logger = Logger.getLogger(RabbitMQService.class);

    publicvoid executeSeckill(String goods){

       String[] good = goods.split("/");

       Long seckillId = Long.parseLong(goods.substring(0, goods.indexOf('/')));

       String userPhone = goods.substring(goods.lastIndexOf('/')+1);

       Date nowTime = new Date();

       Seckill seckill = seckillMapper.queryById(seckillId);

       Date startTime = seckill.getStartTime();

       Date endTime = seckill.getEndTime();

       try {

  //更新库存数量

              int updateCount = seckillMapper.updateReduceNumber(seckillId, nowTime);

              if(updateCount > 0) {

                  //记录购买行为

                  int insertCount = successKilledMapper.insertSuccessKilled(seckillId, userPhone);

              }else {

                  logger.info("手机号为"+userPhone+"的用户秒杀失败");

              }

           }catch (RuntimeException e) {

              //spring事务回滚只对运行期异常起作用

              throw new RuntimeException("seckill error:" + e.getMessage());

           }

    }

}

最后我在前端页面使用倒计时插件增强用户体验效果。







展开阅读全文

没有更多推荐了,返回首页