1. 程式人生 > 程式設計 >如何通過SpringBoot實現商城秒殺系統

如何通過SpringBoot實現商城秒殺系統

這篇文章主要介紹瞭如何通過SpringBoot實現商城秒殺系統,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

學習自:地址

1.主要流程

1.1資料庫:

  

1.2 環境

window下:Zookeeper,Redis,rabbitmq-server。jdk1.8以上。

1.3 介紹

這裡只做秒殺部分功能,其他功能不會涉及。專案執行後可訪問秒殺商品頁面

當用戶沒登陸,點選詳情會跳轉到登陸頁面。

使用者登陸後可以檢視商品的詳情並進行搶購。

注意,使用者對於一件商品只能搶購一次,進行第二次搶購時會被拒絕。當用戶搶購成功時會非同步傳送一封郵件給使用者。

主要邏輯就是以上。接下來看程式碼

1.4 專案結構,api封裝一些列舉和返回值,model主要是實體類和sql對映檔案,service實現業務邏輯程式碼。

  

1.5 顯示秒殺商品到頁面以及使用者的操作使用的還是MVC模式,不細講。主要看如實現高併發下的秒殺。

要細述的話,東西太多,如果想深入瞭解,可點選上面的連結。

基本的秒殺邏輯如下,判斷使用者是否已經搶購過該商品,如果沒有則查詢待秒殺商品詳情,判斷該商品是否可以別秒殺,判斷依據為庫存是否足夠

如果符合條件,則該商品庫存減1,接著,再一次判斷扣減是否成功,如果扣減成功則生成秒殺成功的訂單,同時通知使用者秒殺成功的資訊。

public Boolean killItem(Integer killId,Integer userId) throws Exception {
    Boolean result=false;

    //TODO:判斷當前使用者是否已經搶購過當前商品
    if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
      //TODO:查詢待秒殺商品詳情
      ItemKill itemKill=itemKillMapper.selectById(killId);

      //TODO:判斷是否可以被秒殺canKill=1?
      if (itemKill!=null && 1==itemKill.getCanKill() ){
        //TODO:扣減庫存-減一
        int res=itemKillMapper.updateKillItem(killId);

        //TODO:扣減是否成功?是-生成秒殺成功的訂單,同時通知使用者秒殺成功的訊息
        if (res>0){
          commonRecordKillSuccessInfo(itemKill,userId);

          result=true;
        }
      }
    }else{
      throw new Exception("您已經搶購過該商品了!");
    }
    return result;
  }

程式碼優化1:使用redis的分散式鎖,使用當前秒殺商品的id和當前使用者的id組成一個key,使用StringBuffer拼接,使用雪花演算法生成一個value,存進redis中。

@Autowired
  private StringRedisTemplate stringRedisTemplate;
  /**
   * 商品秒殺核心業務邏輯的處理-redis的分散式鎖
   * @param killId
   * @param userId
   * @return
   * @throws Exception
   */
  @Override
  public Boolean killItemV3(Integer killId,Integer userId) throws Exception {
    Boolean result=false;

    if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){

      //TODO:藉助Redis的原子操作實現分散式鎖-對共享操作-資源進行控制
      ValueOperations valueOperations=stringRedisTemplate.opsForValue();
      final String key=new StringBuffer().append(killId).append(userId).append("-RedisLock").toString();
      final String value=RandomUtil.generateOrderCode();
      Boolean cacheRes=valueOperations.setIfAbsent(key,value); //luna指令碼提供“分散式鎖服務”,就可以寫在一起
      //TOOD:redis部署節點宕機了
      if (cacheRes){
        stringRedisTemplate.expire(key,30,TimeUnit.SECONDS);

        try {
          ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
          if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
            int res=itemKillMapper.updateKillItemV2(killId);
            if (res>0){
              commonRecordKillSuccessInfo(itemKill,userId);

              result=true;
            }
          }
        }catch (Exception e){
          throw new Exception("還沒到搶購日期、已過了搶購時間或已被搶購完畢!");
        }finally {
          if (value.equals(valueOperations.get(key).toString())){
            stringRedisTemplate.delete(key);
          }
        }
      }
    }else{
      throw new Exception("Redis-您已經搶購過該商品了!");
    }
    return result;
  }

程式碼優化2:將 Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS); 每隔30秒判斷當前使用者是否超時寫在了鎖外面,不會因為一次卡頓而影響整個程式。

@Autowired
  private RedissonClient redissonClient;

  /**
   * 商品秒殺核心業務邏輯的處理-redisson的分散式鎖
   * @param killId
   * @param userId
   * @return
   * @throws Exception
   */
  @Override
  public Boolean killItemV4(Integer killId,Integer userId) throws Exception {
    Boolean result=false;

    final String lockKey=new StringBuffer().append(killId).append(userId).append("-RedissonLock").toString();
    RLock lock=redissonClient.getLock(lockKey);

    try {
      Boolean cacheRes=lock.tryLock(30,TimeUnit.SECONDS);
      if (cacheRes){
        //TODO:核心業務邏輯的處理
        if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
          ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
          if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
            int res=itemKillMapper.updateKillItemV2(killId);
            if (res>0){
              commonRecordKillSuccessInfo(itemKill,userId);

              result=true;
            }
          }
        }else{
          throw new Exception("redisson-您已經搶購過該商品了!");
        }
      }
    }finally {
      lock.unlock();
      //lock.forceUnlock();
    }
    return result;
  }

程式碼優化3:

@Autowired
  private CuratorFramework curatorFramework;

  private static final String pathPrefix="/kill/zkLock/";

  /**
   * 商品秒殺核心業務邏輯的處理-基於ZooKeeper的分散式鎖
   * @param killId
   * @param userId
   * @return
   * @throws Exception
   */
  @Override
  public Boolean killItemV5(Integer killId,Integer userId) throws Exception {
    Boolean result=false;

    InterProcessMutex mutex=new InterProcessMutex(curatorFramework,pathPrefix+killId+userId+"-lock");
    try {
      if (mutex.acquire(10L,TimeUnit.SECONDS)){

        //TODO:核心業務邏輯
        if (itemKillSuccessMapper.countByKillUserId(killId,userId);
              result=true;
            }
          }
        }else{
          throw new Exception("zookeeper-您已經搶購過該商品了!");
        }
      }
    }catch (Exception e){
      throw new Exception("還沒到搶購日期、已過了搶購時間或已被搶購完畢!");
    }finally {
      if (mutex!=null){
        mutex.release();
      }
    }
    return result;
  }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。