<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      大廠員工,手把手教你開發(fā)一個高并發(fā)、高可用的營銷活動

      前言

      這幾年工作中做過不少營銷活動,無論是電商業(yè)務(wù)、支付業(yè)務(wù)、還是信貸業(yè)務(wù),營銷在整個業(yè)務(wù)發(fā)展過程中都是必不可少的。如果前期營銷宣傳到位,會給業(yè)務(wù)帶來一波不小的流量。那么作為技術(shù),如何接住這波流量,而不是服務(wù)被打掛。今天大廠員工,手把手教你開發(fā)出一個高并發(fā)、高可用的營銷活動。

      體驗

      點我 - 體檢地址

      點我 - github源碼



      業(yè)務(wù)

      任何脫離業(yè)務(wù)的技術(shù)都是無用功,所以我們先簡單介紹一下業(yè)務(wù)。

      業(yè)務(wù)希望我們的用戶在比如購買商品,下單支付等場景,轉(zhuǎn)化率盡可能的高。那么為了獎勵和刺激用戶,我們希望通過一些優(yōu)惠券的方式,來激勵符合我們規(guī)則的用戶,進(jìn)行下單,進(jìn)行支付,進(jìn)行借錢,進(jìn)行購物等等后續(xù)操作。

      比如某個用戶符合我們活動的規(guī)則,第一步我們會給他展示優(yōu)惠信息,激勵他來進(jìn)行下一步、完成這個任務(wù),然后在給他發(fā)獎、核銷等后續(xù)動作

      校驗抽獎資格

      那么根據(jù)以上我們業(yè)務(wù)的分析,我們第一步就是,用戶進(jìn)來,我們查詢活動,并且校驗用戶是否有資格參加活動。如果有多個活動,我們根據(jù)業(yè)務(wù)規(guī)則選擇一個活動讓用戶進(jìn)行參與。

      那么這也是我們營銷活動的起點,第一步。如果成千上百萬的用戶一下子涌進(jìn)來,我們?nèi)ゲ樵償?shù)據(jù)庫活動信息,并且校驗規(guī)則,我們的數(shù)據(jù)庫瞬間就會崩掉。所以我們的核心思路是:逐級分流,逐步分散流量。通過備份、限流、降級、熔斷等手段提升可用性。

      首先就是加緩存,對于一些靜態(tài)頁面,css,js等文件,可以放在客戶端緩存或者CDN里面。對于活動信息以及規(guī)則,在活動上線之前,將這些信息緩存到redis里面。用戶進(jìn)來時,我們直接取redis里面查詢活動信息,并且計算活動規(guī)則,全程不需要和數(shù)據(jù)庫進(jìn)行交互。最后,評估活動qps,進(jìn)行降級限流,如果流量過大,直接進(jìn)行攔截,防止系統(tǒng)雪崩。

      public MktActivityInfo checkActivityRule(String phone) {
          // 從redis緩存中取
          MktActivityInfo activityInfo = activityCacheService.getActivityInfo();
          if (activityInfo == null || StringUtils.isEmpty(activityInfo.getActivityId())) {
              return null;
          }
      
          ActivityRuleContext context = new ActivityRuleContext();
          context.setPhone(phone);
          // redis緩存中取
          List<MktActivityRule> mktActivityRules = activityCacheService.listActivityRule(activityInfo.getActivityId());
          for (MktActivityRule mktActivityRule : mktActivityRules) {
              BaseRuleService baseRuleService = BaseRuleFactory.getBaseRuleService(mktActivityRule.getRuleKey());
              if (baseRuleService == null || !baseRuleService.check(context)) {
                  return null;
              }
          }
      
          return activityInfo;
      }
      

      抽獎

      一般到達(dá)抽獎,基本都是完成了前面的任務(wù),比如支付,下單等等,最終獲得抽獎資格

      1. 減庫存。將獎品的庫存信息提前緩存到redis里面,比如獎品100個緩存到redis里面。如果有100W人來搶100個獎品,最終也只有100個人通過redis的校驗
      Long num = RedisUtils.decr(CACHE_MKT_ACTIVITY_PRIZE_NUM, stringRedisTemplate);
      if (num == null || num < 0) {
      
          // 將redis庫存加回,可做可不做,看業(yè)務(wù)需求
          RedisUtils.incr(CACHE_MKT_ACTIVITY_PRIZE_NUM, stringRedisTemplate);
          throw new RuntimeException("redis庫存不足 - " + ERROR_MSG);
      }
      
      1. 根據(jù)業(yè)務(wù)場景,如果不是必中獎。在減庫存之前,做一個隨機(jī)數(shù)。如果在隨機(jī)數(shù)之外,直接返回”獎品被搶完“,限制大部分流量進(jìn)入到redis減庫存
      int seed = ThreadLocalRandom.current().nextInt(0, 100) + 1; // 1-100
      int random = NumberUtils.toInt(RedisUtils.get(CACHE_MKT_ACTIVITY_PRIZE_RANDOM, stringRedisTemplate));
      if (seed > random) {
          //log.warn("隨機(jī)比例被攔截 seed = {}, random = {}", seed, random);
          throw new RuntimeException("隨機(jī)比例攔截 - " + ERROR_MSG);
      }
      
      1. 放棄重試
        失敗重試會影響系統(tǒng)性能,重試次數(shù)越多,對系統(tǒng)性能的影響越大。
        抽獎過程中,從抽獎信息驗證到扣庫存、中獎信息入庫的整個過程中,任何一個環(huán)節(jié)異常或失敗,我們都不會進(jìn)行重試,全部當(dāng)做未中獎處理

      2. 防止獎品超發(fā)
        一般我們會通過樂觀鎖,悲觀鎖,分布式鎖來解決。其中樂觀鎖的效率是最高的。
        下面sql不是標(biāo)準(zhǔn)的樂觀鎖,標(biāo)準(zhǔn)的樂觀鎖使用一個version字段來判斷。不過下面的sql能很好的解決樂觀鎖容易失敗的弊端

      update mkt_activity_prize set num = num - 1 where num  >= 1
      
      // 4. 真正數(shù)據(jù)庫減庫存,并且插入發(fā)獎記錄
      // 如果redis預(yù)減庫存成功,這里大概率會成功,基本不會失敗,如果失敗,放棄重試,失敗重試會影響系統(tǒng)性能,重試次數(shù)越多,對系統(tǒng)性能的影響越大。
      Boolean execute = transactionTemplate.execute(status -> {
          // 4.1 扣減庫存
          Integer update = mktActivityPrizeDao.occupyActivityPrize(activityPrize.getActivityId(), activityPrize.getPrizeId());
          if (update == null || update <= 0) {
              //log.warn("mysql 扣減庫存失敗 update = {}", update);
              throw new RuntimeException("mysql庫存扣減失敗 - " + ERROR_MSG);
          }
      
          // 4.2 插入發(fā)獎記錄
          MktActivityPrizeGrant grant = buildMktActivityPrizeGrant(phone, activityPrize);
          Integer insert = mktActivityPrizeGrantDao.insert(grant);
          if (insert == null || insert <= 0) {
              //log.warn("mysql 插入發(fā)獎記錄失敗 insert = {}", insert);
              throw new RuntimeException("mysql 插入發(fā)獎記錄失敗 - " + ERROR_MSG);
          }
      
          return true;
      });
      

      那么從以上幾個步驟我們可以看出,在真正的數(shù)據(jù)庫減少庫存的時候,隨機(jī)攔截 + redis減庫存已經(jīng)幫我們攔截了大部分流量了,也就只有少部分流量會進(jìn)入到我們真正的減庫存環(huán)節(jié)。如果減庫存的流量還是特別的大,我們還可以調(diào)整隨機(jī)比列,同時減庫存可以放到mq中,直接異步化發(fā)放獎品,基本少整個流程不會與數(shù)據(jù)庫進(jìn)行交互,瓶頸點幾乎可以說是沒有。這種架構(gòu),支撐百萬,千萬qps一點問題都沒有。

      最后

      本文根據(jù)真實的業(yè)務(wù)場景,詳細(xì)的剖析了一場營銷活動從技術(shù)的角度如何設(shè)計規(guī)劃,做到真正的高并發(fā),高可用,支撐業(yè)務(wù)穩(wěn)定的運(yùn)行。其中涉及到的技術(shù)點還是比較多的,很多細(xì)節(jié)沒有一一列舉,包括如何保證redis庫存和mysql一致,如果業(yè)務(wù)在活動中想修改庫存怎么辦,怎么保證不重復(fù)領(lǐng)取等等問題。
      強(qiáng)烈建議大家有空可以自己實現(xiàn)一版,其中的一些細(xì)節(jié)還是非常考驗技術(shù)的,實現(xiàn)下來,一定會有不少的收獲,謝謝大家。

      posted @ 2024-08-27 21:39  程序員博博  閱讀(1003)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 免费无码va一区二区三区| 久久综合久色欧美综合狠狠| 亚洲第一综合天堂另类专| 国产在线乱子伦一区二区| 老司机亚洲精品一区二区| 亚洲色大成网站WWW永久麻豆| 少妇无套内谢免费视频| 欧洲美熟女乱又伦免费视频 | 久久这里有精品国产电影网| 亚洲国产精品ⅴa在线观看| 九九热在线免费视频观看| 国产一区二区三区黄色片| 狠狠色狠狠色综合日日不卡| 九九久久自然熟的香蕉图片| 久久综合免费一区二区三区| 日本成熟少妇喷浆视频| 精品综合久久久久久97| 久久羞羞色院精品全部免费| 午夜一区欧美二区高清三区| 小嫩批日出水无码视频免费| 国产精品午夜福利免费看| 一本大道无码av天堂| 高清中文字幕一区二区| 黑人巨大亚洲一区二区久| 国产亚洲第一精品| 99在线视频免费观看| 亚洲天堂亚洲天堂亚洲天堂| 亚洲色最新高清AV网站| 国产乱人伦真实精品视频| 无码免费大香伊蕉在人线国产| 99精品国产丝袜在线拍国语| 久久精品国产色蜜蜜麻豆| 久久月本道色综合久久| 波多野结衣美乳人妻hd电影欧美 | 国内少妇人妻偷人精品| 亚洲成a人片在线观看久| 欧美国产日韩久久mv| 亚洲精品电影院| 亚洲国产精品成人av网| 精品人妻伦九区久久aaa片| 日韩一区二区在线看精品|