欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Redis實(shí)現(xiàn)搶紅包和發(fā)紅包功能

 更新時(shí)間:2024年04月17日 10:09:54   作者:京東云開(kāi)發(fā)者  
搶紅包是我們生活常用的社交功能, 這個(gè)功能最主要的特點(diǎn)就是用戶(hù)的并發(fā)請(qǐng)求高, 在系統(tǒng)設(shè)計(jì)上, 可以使用非常多的辦法來(lái)扛住用戶(hù)的高并發(fā)請(qǐng)求, 在本文中簡(jiǎn)要介紹使用Redis緩存中間件來(lái)實(shí)現(xiàn)搶紅包算法,需要的朋友可以參考下

簡(jiǎn)介:

搶紅包是我們生活常用的社交功能, 這個(gè)功能最主要的特點(diǎn)就是用戶(hù)的并發(fā)請(qǐng)求高, 在系統(tǒng)設(shè)計(jì)上, 可以使用非常多的辦法來(lái)扛住用戶(hù)的高并發(fā)請(qǐng)求, 在本文中簡(jiǎn)要介紹使用Redis緩存中間件來(lái)實(shí)現(xiàn)搶紅包算法, Redis是一個(gè)在內(nèi)存中基于 [key, value] 的緩存數(shù)據(jù)庫(kù), Redis官方性能描述非常高, 所以面對(duì)高并發(fā)場(chǎng)景, 使用Redis來(lái)克服高并發(fā)壓力是一個(gè)不錯(cuò)的手段, 本文主要基于Redis來(lái)實(shí)現(xiàn)基本的搶紅包系統(tǒng)設(shè)計(jì).

發(fā)紅包模塊:

1:發(fā)紅包模塊流程圖如下:

用戶(hù)首先輸入紅包金額和紅包個(gè)數(shù), 然后生成當(dāng)前紅包唯一標(biāo)識(shí), 并使用二倍均值算法生成隨機(jī)金額的紅包, 然后將生成的紅包存入緩存Redis數(shù)據(jù)庫(kù)中, Redis數(shù)據(jù)庫(kù)中會(huì)保存當(dāng)前剩余的紅包數(shù)量和每個(gè)紅包的金額, 由于Redis數(shù)據(jù)庫(kù)是作為臨時(shí)存儲(chǔ)的地方, 所以發(fā)紅包記錄需要持久化存儲(chǔ)在數(shù)據(jù)庫(kù)中, 這里為加快系統(tǒng)響應(yīng), 使用異步的方式, 將紅包金額紀(jì)錄存儲(chǔ)入Mysql數(shù)據(jù)庫(kù)中, 以上就是發(fā)紅包模塊的簡(jiǎn)要系統(tǒng)設(shè)計(jì).

2:隨機(jī)生成紅包金額

對(duì)于搶紅包來(lái)說(shuō), 生成紅包金額是非常關(guān)鍵的, 這里有許多生成隨機(jī)數(shù)方法, 在本文中介紹一種使用較多的二倍均值算法來(lái)隨機(jī)生成紅包金額.對(duì)于搶紅包來(lái)說(shuō), 如果發(fā)送一個(gè)金額為J的紅包, 那么對(duì)與搶紅包的N個(gè)人來(lái)說(shuō), 公平的概率是: 每個(gè)人搶到J / N 的金額的概率是相同的, 例如100元紅包發(fā)給10個(gè)人,那么最公平的策略是使每個(gè)人搶到10元的概率相同, 二倍均值算法就是基于上面這個(gè)概率策略. 二倍均值算法流程如下: 首先設(shè)置紅包金額為J, 搶紅包人數(shù)為N, 接下來(lái)計(jì)算隨機(jī)數(shù)區(qū)間上U = J / N * 2, 得到隨機(jī)數(shù)區(qū)間(0,U), 從而在這個(gè)區(qū)間里生成第一個(gè)隨機(jī)數(shù)金額M, 接下來(lái)繼續(xù)生成第二個(gè)隨機(jī)金額. 首先更新總紅包金額為J-M,總搶紅包人數(shù)為N-1, 然后生成第二個(gè)隨機(jī)金額區(qū)間(0, (J-M) / (N-1) *2) , 從這個(gè)區(qū)間里面生成第二個(gè)隨機(jī)金額M2, 繼續(xù)迭代, 直到生成最后一個(gè)紅包金額, 下圖是二倍均值算法的流程

二倍均值算法案例: 紅包總金額100元, 總計(jì)10個(gè)人

計(jì)算第一個(gè)隨機(jī)金額區(qū)間: 100/10X2 = 20, 第一個(gè)隨機(jī)金額的區(qū)間是(0,20 ),區(qū)間均值為10

假設(shè)第一個(gè)人搶到10元,剩余金額是90 元

計(jì)算第二個(gè)隨機(jī)金額區(qū)間: 90/9X2 = 20, 第一個(gè)隨機(jī)金額的區(qū)間是(0,20 ),區(qū)間均值為10

假設(shè)第二個(gè)人搶到10元,剩余金額是80 元 計(jì)算第三個(gè)隨機(jī)金額區(qū)間: 80/8X2 = 20, 第一個(gè)隨機(jī)金額的區(qū)間是(0,20 ),區(qū)間均值為10

...............

所以使用二倍均值算法能夠在不論誰(shuí)先搶的情況下, 都能公平保證每個(gè)人搶到平均金額的概率是相等的, 二倍均值算法生成紅包金額的代碼如下:

//這里輸入的totalMoney單位是分,例如100元,totalMoney = 10000
public List<Integer> getRedPackage(Integer totalMoney,Integer totalPeopleCount) {
    List<Integer> moneyList = new ArrayList<>();
    //暫存剩余金額為紅包的總金額
    Integer restMoney = totalMoney;
    //暫存剩余的總?cè)藬?shù)-初始化時(shí)即為指定的總?cè)藬?shù)
    Integer restPeopleCount = totalPeopleCount;    
    //隨機(jī)數(shù)對(duì)象
    Random random = new Random();
    //開(kāi)始循環(huán)迭代生成紅包
    for (int i =0;i< totalPeopleNum-1;i++){
       //加1是為了至少搶到1分錢(qián)
       int money = random.nextInt (restMoney / restPeopleCount * 2) + 1;
       restMoney -= money;
       restPeopleCount--;
       moneyList.add(money);
    }
    //添加最后的一個(gè)紅包金額
    amountList.add(restAmount);
    return amountList;
}

3: 紅包存儲(chǔ)

為了應(yīng)對(duì)用戶(hù)高并發(fā)的請(qǐng)求, 也就是需要頻繁讀取紅包金額和數(shù)量, 所以將紅包金額和數(shù)量存儲(chǔ)在Mysql中是不行的, 所以只能借助基于內(nèi)存的Redis數(shù)據(jù)庫(kù)來(lái)支持高并發(fā)的讀取操作.Redis中有5種基本的數(shù)據(jù)結(jié)構(gòu)分別是:String, List, Set, Sorted Set, Map這五種, 紅包金額數(shù)量是一個(gè)List集合, 所以使用List來(lái)存儲(chǔ)最為合適,在發(fā)紅包時(shí), 我們先用二倍均值算法隨機(jī)生成一定數(shù)量的紅包金額, 然后將紅包金額和紅包數(shù)量存入Redis緩存中,等待用戶(hù)搶紅包

//隨機(jī)生成全局唯一的紅包id
redId = getRedId();
//首先生成紅包金額
List<Integer> moneyList = getRedPackage(totalMoney,totalPeopleCount);
//放入redis
redisClient.lpush(redId, moneyList);
//redis中記錄紅包個(gè)數(shù)
redisClient.set(redId, moneyList.size());
//異步存儲(chǔ)發(fā)紅包記錄到Mysql數(shù)據(jù)庫(kù)
//將紅包id返回
return redId;

搶紅包模塊:

1:搶紅包模塊流程圖如下:

首先判斷用戶(hù)是否已經(jīng)搶過(guò)紅包了, 是否還有剩余的紅包, 如果搶過(guò)或者剩余紅包數(shù)量小于等于0, 則代表紅包已經(jīng)被搶完了, 直接結(jié)束用戶(hù)本次搶紅包流程. 如果還有剩余的紅包數(shù)量, 則從Redis緩存列表中彈出一個(gè)紅包金額, 然后將剩余紅包數(shù)量減1, 同時(shí)異步將用戶(hù)搶紅包記錄存入Mysql數(shù)據(jù)庫(kù), 最后將搶到的紅包金額返回給用戶(hù), 結(jié)束本次搶紅包流程

2:首先判斷是否已經(jīng)搶過(guò)紅包

通過(guò)在Redis中以用戶(hù)ID構(gòu)建一個(gè)唯一Key來(lái)判斷是否搶過(guò)紅包, Key的構(gòu)建規(guī)則是:業(yè)務(wù)前綴+紅包id+用戶(hù)id

redMoney = redisClient.get("rob" + redId + useId)
//如果不為空,則說(shuō)明已經(jīng)搶過(guò)了,直接返回?fù)屵^(guò)的紅包金額
if (redMoney != null) {
    return redMoney
}

3:判斷是否還有紅包

通過(guò)在Redis中以紅包id記錄一個(gè)數(shù)量來(lái)判斷是否還有紅包, key的構(gòu)建規(guī)則是:業(yè)務(wù)前綴+紅包id

totalNum = redisClient.get("totalNum" + redId)
//如果為空或者小于等于0則代表沒(méi)有了
if (totalNum == null || totalNum <= 0) {
    return null
}

4:彈出一個(gè)紅包金額

因?yàn)槲覀兪前鸭t包金額存儲(chǔ)到Redis的List列表中的, 所以直接使用列表的Pop操作就行了

money = redisClient.rpop(redId)
//如果不為空,則說(shuō)明搶到了
if (money != null) {
    ....
    紅包個(gè)數(shù)減1
    存儲(chǔ)搶紅包記錄
    設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包
    ....
    //返回?fù)尩降慕痤~
    return money
}
//沒(méi)搶到
return null

5:減少紅包個(gè)數(shù)

紅包總數(shù)是以一個(gè)[key, value] 鍵值對(duì)存儲(chǔ)在Redis中的, 所以這里使用Redis的DECR命令就行了

money = redisClient.rpop(redId)
//如果不為空,則說(shuō)明搶到了
if (money != null) {
    //紅包個(gè)數(shù)減1
    redisClient.decr(redId)
    ....
    存儲(chǔ)搶紅包記錄
    設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包
    ....
    //返回?fù)尩降慕痤~
    return money
}
//沒(méi)搶到
return null

6:異步記錄搶紅包記錄

采用異步的方式將記錄存入Mysql數(shù)據(jù)庫(kù), 異步的方式可以采用消息隊(duì)列或者多線(xiàn)程的方式來(lái)實(shí)現(xiàn)

money = redisClient.rpop(redId)
//如果不為空,則說(shuō)明搶到了
if (money != null) {
    //紅包個(gè)數(shù)減1
    redisClient.decr(redId)
    //異步存儲(chǔ)搶紅包記錄
    這里可以使用mq或者多線(xiàn)程的方式來(lái)實(shí)現(xiàn)
    ....
    設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包
    ....
    //返回?fù)尩降慕痤~
    return money
}
//沒(méi)搶到
return null

7:設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包

money = redisClient.rpop(redId)
//如果不為空,則說(shuō)明搶到了
if (money != null) {
    //紅包個(gè)數(shù)減1
    redisClient.decr(redId)
    //異步存儲(chǔ)搶紅包記錄
    這里可以使用mq或者多線(xiàn)程的方式來(lái)實(shí)現(xiàn)
    //設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包
    redisClient.set("rob" + redId + useId, money)
    //返回?fù)尩降慕痤~
    return money
}
//沒(méi)搶到
return null

8: 整體的偽代碼邏輯如下:

redMoney = redisClient.get("rob" + redId + useId)
//如果不為空,則說(shuō)明已經(jīng)搶過(guò)了,直接返回?fù)屵^(guò)的紅包金額
if (redMoney != null) {
    return redMoney
}
totalNum = redisClient.get("totalNum" + redId)
//如果紅包總數(shù)小于0, 則代表已經(jīng)搶完了, 直接返回空
if (totalNum == null || totalNum <= 0) {
    return null
}
money = redisClient.rpop(redId)
//如果不為空,則說(shuō)明搶到了
if (money != null) {
    //紅包個(gè)數(shù)減1
    redisClient.decr(redId)
    //異步存儲(chǔ)搶紅包記錄
    這里可以使用mq或者多線(xiàn)程的方式來(lái)實(shí)現(xiàn)
    //設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包
    redisClient.set("rob" + redId + useId, money)
    //返回?fù)尩降慕痤~
    return money
} 
//沒(méi)搶到
return null

9:分布式鎖

這里涉及到了同一個(gè)用戶(hù)多次高并發(fā)來(lái)?yè)尲t包的情況, 并且代碼邏輯中包含了下面這種邏輯: 判斷條件成立然后進(jìn)行業(yè)務(wù)操作,最后設(shè)置條件. 這種業(yè)務(wù)邏輯如果不防止并發(fā)的話(huà), 就會(huì)產(chǎn)生重復(fù)操作, 所以需要使用鎖來(lái)限制每一個(gè)用的訪(fǎng)問(wèn)頻率, 加鎖的方式是使用分布式鎖, 這是因?yàn)槲覀儞尲t包服務(wù)不可能只在一臺(tái)服務(wù)器上部署, 同時(shí)基于Redis也能很容易的實(shí)現(xiàn)分布式鎖, 使用Redis命令setNx命令就可以實(shí)現(xiàn)簡(jiǎn)單分布式鎖

redMoney = redisClient.get("rob" + redId + useId)
//如果不為空,則說(shuō)明已經(jīng)搶過(guò)了,直接返回?fù)屵^(guò)的紅包金額
if (redMoney != null) {
    return redMoney
}
totalNum = redisClient.get("totalNum" + redId)
//如果紅包總數(shù)小于0, 則代表已經(jīng)搶完了, 直接返回空
if (totalNum == null || totalNum <= 0) {
    return null
}
//加分布式鎖
lockResut = redisClient.setNx(useId,redId,timeOut);
//加鎖失敗,直接返回
if(!lockResult){
    return;
}
try{
    money = redisClient.rpop(redId)
    //如果不為空,則說(shuō)明搶到了
    if (money != null) {
        //紅包個(gè)數(shù)減1
        redisClient.decr(redId)
        //異步存儲(chǔ)搶紅包記錄
        這里可以使用mq或者多線(xiàn)程的方式來(lái)實(shí)現(xiàn)
        //設(shè)置該用戶(hù)已經(jīng)搶過(guò)紅包
        redisClient.set("rob" + redId + useId, money)
        //返回?fù)尩降慕痤~
        return money
    }     
} finally {
    //刪除鎖
    redisClient.del(useId)
}
//沒(méi)搶到
return null

總結(jié)

以上就是完整的搶紅包偽代碼流程, 可以基本實(shí)現(xiàn)發(fā)紅包以及搶紅包功能, 該方法基于Redis來(lái)實(shí)現(xiàn)紅包的存儲(chǔ)和搶紅包的操作, 基于二倍均值算法來(lái)實(shí)現(xiàn)紅包金額的隨即生成, 在整體功能上還有很多不完善的地方, 可以基于整體框架進(jìn)行擴(kuò)展開(kāi)發(fā), 實(shí)現(xiàn)更加完整的算法

到此這篇關(guān)于基于Redis實(shí)現(xiàn)搶紅包和發(fā)紅包功能的文章就介紹到這了,更多相關(guān)Redis搶紅包和發(fā)紅包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于redigo中PubSub的一點(diǎn)小坑分析

    關(guān)于redigo中PubSub的一點(diǎn)小坑分析

    這篇文章主要給大家介紹了關(guān)于redigo中PubSub的一點(diǎn)小坑的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • redis cluster支持pipeline的實(shí)現(xiàn)思路

    redis cluster支持pipeline的實(shí)現(xiàn)思路

    本文給大家介紹redis cluster支持pipeline的實(shí)現(xiàn)思路,在 cluster 上執(zhí)行 pipeline 可能會(huì)由于 redis 節(jié)點(diǎn)擴(kuò)縮容 中途 redirection 切換連接導(dǎo)致結(jié)果丟失,具體細(xì)節(jié)問(wèn)題請(qǐng)參考下本文
    2021-06-06
  • Redis7.0部署集群的實(shí)現(xiàn)步驟

    Redis7.0部署集群的實(shí)現(xiàn)步驟

    本文主要介紹了Redis7.0部署集群的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Linux服務(wù)器快速安裝Redis6.0步驟示例詳解

    Linux服務(wù)器快速安裝Redis6.0步驟示例詳解

    這篇文章主要為大家介紹了Linux服務(wù)器快速安裝Redis6.0步驟示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 詳解如何使用Redis作為高效緩存

    詳解如何使用Redis作為高效緩存

    Redis是一個(gè)高性能的 內(nèi)存存儲(chǔ)系統(tǒng),通常被用作 緩存 來(lái)加速數(shù)據(jù)訪(fǎng)問(wèn),提高應(yīng)用的吞吐量和響應(yīng)速度,本文詳細(xì)講解如何使用 Redis 作為高效緩存,包括基本原理、常見(jiàn)模式、最佳實(shí)踐以及優(yōu)化技巧,需要的朋友可以參考下
    2025-01-01
  • Redis實(shí)現(xiàn)附近商鋪的項(xiàng)目實(shí)戰(zhàn)

    Redis實(shí)現(xiàn)附近商鋪的項(xiàng)目實(shí)戰(zhàn)

    本文主要介紹了Redis實(shí)現(xiàn)附近商鋪的項(xiàng)目實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis GEO實(shí)現(xiàn)附近搜索功能

    Redis GEO實(shí)現(xiàn)附近搜索功能

    這篇文章主要介紹了Redis GEO實(shí)現(xiàn)附近搜索功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2024-12-12
  • 淺談Redis緩沖區(qū)機(jī)制

    淺談Redis緩沖區(qū)機(jī)制

    本文主要介紹淺談Redis緩沖區(qū)機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 基于session?Redis實(shí)現(xiàn)登錄

    基于session?Redis實(shí)現(xiàn)登錄

    這篇文章主要介紹了基于session?Redis實(shí)現(xiàn)登錄的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • redis字符串類(lèi)型_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    redis字符串類(lèi)型_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了redis字符串類(lèi)型的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08

最新評(píng)論