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中。

Boolean cacheRes=valueOperations.setIfAbsent(key,value);當前鎖唯一,一次只能放一個用戶進行操作,操作結束后,釋放該鎖。
    @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,10,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) <= 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("zookeeper-您已經搶購過該商品了!");
                }
            }
        }catch (Exception e){
            throw new Exception("還沒到搶購日期、已過了搶購時間或已被搶購完畢!");
        }finally {
            if (mutex!=null){
                mutex.release();
            }
        }
        return result;
    }

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM