どのようにSpringBootを通じて商店街の秒殺システムを実現しますか?


この文章は主にSpringBootを通じて商店街の秒殺システムを実現する方法を紹介しています。ここでは例示的なコードで紹介されています。
学習自:住所
1.主な流れ
1.1データベース:

1.2環境
window下:Zookeeper、Redis、rabbitmq-server。jdk 1.8以上です。
1.3紹介
ここでは秒殺の機能だけをしています。他の機能は触れません。プロジェクトの運行後、秒殺商品ページにアクセスできます。

ユーザーがログインしていない場合は、詳細をクリックしてログインページにジャンプします。

ユーザーがログインしたら商品の詳細を確認して買い占めができます。

注意してください。ユーザーは一つの商品に対して一回だけ買います。二回目の買い占めをする時は拒絶されます。ユーザーが買い占めに成功したら、非同期でユーザーにメールを送ります。

主なロジックは以上です。コードを見ます
1.4プロジェクト構造、apiパッケージにはいくつかのエニュメレーションとリターン値があり、モデルは主に実体類と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を構成して、SteringBufferを使ってつなぎ合わせて、雪花アルゴリズムを使って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,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;
  }
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。