日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

小红书-内卖秒杀项目总结

發布時間:2023/12/8 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 小红书-内卖秒杀项目总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、背景介紹
  • 二、鳴謝
  • 三、為什么會決定參與內賣?
  • 四、第一次內賣
    • 1. 前言
    • 2. 技術方案設計
    • 3、內賣過程中遇到的問題
    • 4、回顧總結
  • 四、第二次內賣
    • 1、前言
    • 2、技術方案設計
    • 3、回顧總結

一、背景介紹

公司內部福利社的同事牽頭組織,將福利社的退貨商品,低價售賣給公司內部員工,算是員工福利吧。
內賣舉辦過挺多次了,這里僅記錄我參與的兩次內賣。

二、鳴謝

特別感謝雪兔、小伊、白明、安迪,留白、云流,公生、阿力、路飛、莫一兮
感謝其他在內賣過程中給與各種各樣支持的同事們

三、為什么會決定參與內賣?

套用經典名言:如今機會就在眼前,我不知道何時才能再有機會去參與一個真實的秒殺項目。
對于程序猿來說,秒殺是一個經典的高技術難度場景,絕佳的鍍金項目。
在這里要感謝公司的ExtraMile計劃,才能給與我這個機會,在公司內,去做一些非本職工作的項目,去做一些技術、業務上的挑戰。紙上得來終覺淺,看再多的書籍、博客,如果沒有在真實業務場景下去實現過,就只能叫紙上談兵,吹牛都沒人信的。

四、第一次內賣

1. 前言

第一次內賣,后端開發人員只有我一個。
因為倉庫那邊堆積壓力很大,所以從我接到任務開始,開發時間只有一周多,還要盡量不影響工作,所以技術方案設計的時候,快速實現就很重要,一切不穩定因素都應當剔除。

2. 技術方案設計

  • 緩存:不使用redis,采用單機內存做緩存
    我當時剛跑路到小紅書,在內部基礎設施服務的使用上,接連踩了各種各樣的坑,在時間如此緊張的情況下,我對于接入公司內部redis實在是沒有信心。不僅是redis,內部基礎設施服務都是能不用就不用。

  • 采用java的Semaphore來做限購。
    內賣限制每個人只能購買四件商品,那么就用Semaphore來做令牌發放,獲取到令牌的請求才能進行購買,購買失敗就返回令牌,購買成功則不返回。

  • 限流:用guava的RateLimiter。
    因為是單機器,怕撐不住,所以得加一個限流才行。

  • 內賣整體業務流程
    內賣原則上不支持退貨,用戶收到貨物之后,根據實際到貨情況和貨物質量,掃描官方支付寶付款。
    (流程圖鏈接)

  • 秒殺流程
    每人最多購買四件商品,每件商品限購一件
    (流程圖鏈接)

  • 核心秒殺代碼:

  • /*** 用戶購買商品的數量限制*/ public static volatile Integer userBuyLimit = 0; /*** QPS限流,限流100*/ public static final RateLimiter qpsLimter = RateLimiter.create(100); /*** 空的vector實例*/ private static final Vector emptyVector = new Vector(); /*** 用戶購買的商品*/ private static volatile Map<Integer, Vector<Integer>> userGoodsMap = new ConcurrentHashMap<>(); /*** 商品的存量數據*/ public static volatile Map<Integer, AtomicInteger> goodsRemainCountMap = new HashMap<>(); /*** 用戶的購物令牌,需要獲取到令牌之后,該用戶才可以購物*/ private static volatile Map<Integer, Semaphore> userTokenMap = new HashMap<>(); /*** 商品的可購買令牌,需要獲取到商品的令牌之后,才可以購買該商品*/ private static volatile Map<Integer, Semaphore> goodsTokenMap = new HashMap<>(); public void buy(User operator, Integer goodsId) {// 當前內賣秒殺是否開啟if (!BatchStatusEnum.SEC_kILL.equals(Constant.CURRENT_BATCH_STATUS)) {throw new RuntimeException("內賣秒殺還未開始");}// 用戶購買商品數量是否已到限額Vector vector = userGoodsMap.getOrDefault(operator.getId(), emptyVector);if (vector.size() >= userBuyLimit) {throw new RuntimeException(String.format("您已購買%s件商品,無法再購買", userBuyLimit));}// 用戶是否已購買if (vector.contains(goodsId)) {throw new RuntimeException("每件商品限購一件,您已購買該商品,無法再購買");}// 商品是否還有存量if (goodsRemainCountMap.get(goodsId).get() < 1) {throw new RuntimeException("該商品已被搶購一空");}// QPS限流boolean pass = qpsLimter.tryAcquire();if (!pass) {throw new RuntimeException("競爭太激烈了,請重試");}// 獲取令牌Semaphore userTokens = userTokenMap.get(operator.getId());try {userTokens.tryAcquire();Semaphore goodsTokens = goodsTokenMap.get(goodsId);try {goodsTokens.tryAcquire(10);} catch (Exception ex) {goodsTokens.release();throw ex;}} catch (Exception ex) {logger.warn("秒殺請購失敗:" + ex.getMessage(), ex);userTokens.release();throw new RuntimeException("競爭太激烈了,請重試");}// 購買成功,記錄相關信息userGoodsMap.get(operator.getId()).add(goodsId);goodsRemainCountMap.get(goodsId).decrementAndGet(); }

    3、內賣過程中遇到的問題

  • 內賣剛開始就崩掉了,原因是前端資源加載有瓶頸。
    從來沒寫過前端的我,從來沒想過,前端加載竟然竟然會是個瓶頸,我一直以為只要我后端hold住就萬事大吉了。然后能怎么辦呢?大家就隨緣進入購物頁面了。

  • 秒殺購物體驗很差。
    商品列表沒有展示剩余庫存,也沒有展示已購買訂單,所以大家的購物體驗就是:進入商品列表,然后點點點,買到沒,不知道。

  • 因為都使用機器內存做緩存,所以服務如果重啟就會丟失數據。可是最后生成訂單數據時,意外報錯了。幸好排查之后發現是數據異常導致的,刪除異常數據之后,就能正常生成訂單了。如果是代碼bug的話,那我給大家伙跪下求原諒了。

  • 只關注了主要的秒殺流程,做了各種并發控制,但是用戶注冊、地址填寫等沒有做并發控制,導致一個人多個賬戶、一個賬戶多個收貨地址等數據異常情況。

  • 4、回顧總結

  • 不可重復操作,一定要做好并發控制。
    不能認為在業務流程上不存在并發問題,就不需要做并發限制處理。

  • 完善的測試與壓測。
    問題無法完全避免,但是完善的測試與壓測能幫助我們盡量去避免問題。
    測試與壓測,需要盡可能的去模擬真實用戶的使用場景,這樣才能發現更多的問題,比如前端資源瓶頸。

  • 迫不得已的情況下,選擇機器內存做緩存,這個可以理解,但是沒有做好緩存持久化,導致秒殺開始后,重啟項目就會丟失數據,一切歸零,這是整個方案的最大風險點。

  • 沒有做完善的數據監控,導致時候無法回顧整個秒殺過程中的各種性能指標,尤其是qps,幸好還有第二次內賣,不然裝逼都沒機會了。

  • 四、第二次內賣

    1、前言

    這次時間充裕,后端還有三個人,人力是足夠的。美中不足的是,直到秒殺開始前,也沒找到前端小伙伴,導致只能在以前的后端接口基礎上做修改,原本設想的所有設計前端的優化、新功能點都無法做。

    2、技術方案設計

  • 內賣整體業務流程(鏈接)
  • 秒殺下單流程(鏈接)
    MySQL訂單入庫限流,主要是為了避免MySQL被壓垮
    實際下單落庫,只需要在關系表中增加一個用戶id與商品id的關聯關系即可,MySQL語句是極其簡單的,考慮到用戶量與商品都不是特別大,而且限流之后qps也不會特別高,MySQL的處理速度足以滿足下單需求,所以這里直接同步方式落庫,而不采用異步落庫方式。
  • 核心秒殺代碼:
  • public void buy(User operator, Integer goodsId) {// 內賣是否開始checkBatchProcess();// 商品是否還有庫存String goodsCountKey = String.format(GOODS_REMAIN_FORMAT, goodsId);Integer goodsRemain = cacheService.getIntOrDefault0(goodsCountKey);if (goodsRemain < 1) {throw new RuntimeException("該商品已無庫存");}// 用戶是否還有額度BatchConfig currentBatch = batchConfigService.getCurrentBatch();String userBuyCountKey = String.format(USER_BUY_COUNT, operator.getId(), currentBatch.getId());Integer userBuyCount = cacheService.getIntOrDefault0(userBuyCountKey);if (userBuyCount >= currentBatch.getBuyLimit()) {throw new RuntimeException("您已達到購買限額,無法再購買商品");}// 用戶是否已購買該商品String boughtGoodsKey = String.format(USER_BOUGHT_GOODS, operator.getId());if (cacheService.isMember(boughtGoodsKey, goodsId)) {throw new RuntimeException("您已購買過該商品,每人每件商品限購一件");}// 獲取商品鎖String goodsLockKey = String.format(GOODS_COUNT_MODIFY_LOCK, goodsId);boolean success = false;//if (cacheService.setnx(goodsLockKey, System.currentTimeMillis())) {if (cacheService.set(goodsLockKey, String.valueOf(System.currentTimeMillis()), "NX", "PX", DEFAULT_GODOS_EXPIRE_TIME)) {try {goodsRemain = cacheService.getIntOrDefault0(goodsCountKey);if (goodsRemain < 1) {throw new RuntimeException("該商品已無庫存");}// 獲取用戶鎖String userLock = String.format(USER_BUY_LOCK, operator.getId(), currentBatch.getId());//if (cacheService.setnx(userLock, System.currentTimeMillis())) {if (cacheService.set(userLock, String.valueOf(System.currentTimeMillis()), "NX", "PX", DEFAULT_USER_EXPIRE_TIME)) {try {userBuyCount = cacheService.getIntOrDefault0(userBuyCountKey);if (userBuyCount >= currentBatch.getBuyLimit()) {throw new RuntimeException("您已達到購買限額,無法再購買商品");}// MySQL限流int mysqlQpsLimit = ConfigService.getAppConfig().getIntProperty("redersale.mysql.qps.limit", 500);try {if (cacheService.incrBy(MYSQL_QPS_LIMIT, 1) > mysqlQpsLimit) {throw new RuntimeException("購買失敗,請重試");}// 成功購買商品cacheService.setByDefaultExpire(goodsCountKey, goodsRemain - 1);cacheService.setByDefaultExpire(userBuyCountKey, userBuyCount + 1);cacheService.addMemberToSet(boughtGoodsKey, goodsId);goodsOrderMapper.insertOrder("秒殺下單", currentBatch.getId(), operator.getId(),operator.getRedName(), goodsId);success = true;} finally {cacheService.incrBy(MYSQL_QPS_LIMIT, -1);}// 獲取mysql的qps} finally {cacheService.delete(userLock);}}} finally {cacheService.delete(goodsLockKey);}}if (!success) {throw new RuntimeException("搶購失敗,請重試");} }

    3、回顧總結

  • 主要指標:
    • 峰值qps:3k
    • 賣出商品sku數量:2313
    • 生成訂單數量:11238
    • 售賣總金額:609,653
  • 相比第一次,不僅秒殺期間,整個app沒有崩潰,而且秒殺使用體驗也比第一次好了很多,算是比較成功了。
  • 數據監控
    做任何活動,需要充分考慮到運維監控的需求,作為業務方,需要知道當前關鍵業務數據的趨勢情況,作為技術方,需要知道當前關鍵技術指標情況(判斷服務是否還能支撐的住)。在第二次內賣中,考慮到了數據監控的需求,這點不錯,但是部分數據監控是人工每次sql查詢的,可以改成程序自動查詢并在相關業務群里報數會更好點。
  • 異常處理預案
    這次內賣提前準備了預案,比如當用戶鎖未正常釋放時,手動為用戶釋放等等,這也是比較可喜的進步了。
  • 技術評審
    復雜、重要的業務,需要有技術評審環節,群策群力,獨自一人制定方案,難免會有各種遺漏。
  • 信息溝通
    要有一個統一的群,用來活動各方及時溝通各種信息。所有相關信息,要有文檔記錄,并且文檔目錄要放在溝通群的公告中,方便隨時查找。
  • 總結

    以上是生活随笔為你收集整理的小红书-内卖秒杀项目总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。