1. 程式人生 > >RabbitMQ在秒殺場景中的簡單應用

RabbitMQ在秒殺場景中的簡單應用

秒殺業務的核心是庫存處理,使用者購買成功後會進行減庫存操作,並記錄購買明細。當秒殺開始時,大量使用者同時發起請求,這是一個並行操作,多條更新庫存數量的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());

           }

    }

}

最後我在前端頁面使用倒計時外掛增強使用者體驗效果。