天天看點

SpringBoot 結合 Redis實作搶紅包功能

前言

最近在工作中,新接了一個紅包功能的需求,這對于我來說,是非常感興趣的,但是又有點害怕,害怕自己做不好,也沒有了解過搶紅包功能的實作,最後隻能靠着百度,查詢到了幾篇非常好的文章,感謝大神的無私奉獻,對我來說簡直就是雪中送炭.

https://www.zybuluo.com/yulin718/note/93148

https://juejin.cn/post/6925947709517987848

需求

我這邊的業務是在直播房間類發送金币紅包,首先金币是沒有小數點的,是以不用考慮小數點的問題,一般就是最小就發 10金币。紅包類型分為 房間紅包和全服紅包,差別在于,房間紅包隻能在這個房間的人能搶,而全服紅包就是所有人都能搶,紅包又分為 普通紅包和随機紅包,普通紅包就簡單了,沒人平均去分,随機紅包就要用到微信的紅包算法了 額度在0.01和剩餘平均值*2之間 。

流程圖

SpringBoot 結合 Redis實作搶紅包功能

紅包功能分為兩種實作,一種就是預配置設定 紅包,一種就是純記憶體計算,其實這種實作都可以,在看了微信紅包的架構中寫到 微信金額是拆的時候實時算出來,不是預先配置設定的,采用的是純記憶體計算,不需要預算空間存儲 ,這也就表示 微信紅包是純記憶體計算出來的,這種情況對于機器的預算是非常吃緊的,一般來說小的公司可以忽略這些,比較流量也不大,直接就用純記憶體計算就可以,預配置設定 : 在開獎之前就已經知道那些人能搶到紅包了,或者說有搶到紅包的資格,比如: 我們在看一些直播的時候,有一些搶紅包操作,但是通常都會說,先買一件商品才能搶,又或者,直播發紅包,要先點關注才能搶到紅包,這就是預配置設定法,這種好處就是,能抵擋一波流量。

Redis實作

實作以上功能最簡單的就是 使用 redis中的 list 結構來做了,先進行紅包拆分,然後寫入到 redis中,沒一個人來搶的時候就pop出去,一直到沒有就表示該紅包被搶光了,這裡建議大家看看,我發的連結上面有多紅包各種實作的分析,非常好,并且也說了這種方式的弊端:搶紅包時一旦從隊列彈出,此時系統崩潰,恢複後此隊列中的紅包明細資訊已丢失,需要人工補償。 确實這就是這種實作方式最大的問題,不過,小公司也不用考慮這些,大不了就做redis的高可用。

代碼實作

發紅包,紅包拆分:

/**
     * redis list 結構實作 lpush 發紅包:
     * @param type 0普通紅包 1.随機紅包
     * @param number 紅包個數
     * @param amount 紅包金額
     */
    @PostMapping("/redWars")
    public void redWars(Integer type,Integer number, Integer amount){
        ArrayList<Integer> arrayList = new ArrayList<>();
        //總金額: 發送紅包的總金币數
        Integer money = number * amount;
        //普通紅包拆分
        if(type == 0){
            arrayList = commonRed(money, number);
            //随機紅包拆分
        }else if(type == 1){
            /**
             * 由于我們金币是不需要小數的,我們值需要整數,是以下面代碼被我改了
             * 如果是金額的紅包 請 參考 RoomTest
             */
            arrayList = commonRoomRed(BigDecimal.valueOf(money),number);
        }
        //類型轉換:
        List<String> collect = arrayList.stream().map(i -> String.valueOf(i)).collect(Collectors.toList());
        //紅包key 過期時間 拆分的資料: 這就是搶紅包的金額:
        redisService.lpush("1002",30, TimeUnit.HOURS,collect);
    }

           

搶紅包:

/**
     *  list 資料接口給 lpop: 搶紅包
     * @param key 紅包key
     */
    @RequestMapping("/setLpop")
    public void setLpop(String key){
        String lpop = redisService.lpop(key);
        if(!StringUtils.hasLength(lpop)){
            System.out.println("目前紅包已搶光!");
        }else{
            System.out.println("恭喜你搶到紅包--->"+lpop);
        }
    }

           

完整代碼: spring-boot-redis-integrat

執行個體代碼在github上面需要的請自行拉取:spring-boot-integrate 然後後續會內建更多的子產品進去,需要請點個star。後續會內建更多的接口實作,有需要的請儲存。

如果這篇文章,有幫助到大家的,請給作者一個一鍵三連,謝謝