最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis迷你版微信搶紅包實戰(zhàn)

 更新時間:2025年05月25日 11:37:40   作者:NPE~  
本文主要介紹了Redis迷你版微信搶紅包實戰(zhàn)

全部代碼:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/redis_demo/redpacket_demo

1 思路分析

搶紅包是一個高并發(fā)操作,且我們需要保證其原子性,同時搶紅包過程中不能加鎖,不能出現(xiàn)因為某個人網(wǎng)絡卡頓,導致其他人無法搶紅包??。

1.1 流程

搶紅包流程:發(fā)紅包-拆紅包-搶紅包-記錄誰搶了紅包

  • 發(fā)紅包:提供接口send,參數(shù):紅包總金額,紅包個數(shù)。拆完之后通過redis list結(jié)構(gòu)將紅包存入redis
  • 拆紅包:split接口,根據(jù)算法將紅包合理的拆分,金額不能差距太大,比如:一個100元紅包,拆分為20個,不能出現(xiàn)一個紅包里就包含99元的情況
  • 搶紅包:提供rob接口,接收紅包名(要搶哪個紅包,不同人不同群發(fā)的紅包都是唯一的),接收用戶id(誰搶)
  • 記錄:搶完紅包之后,記錄用戶id與所搶紅包??對應關系,防止多搶。通過redis hset數(shù)據(jù)結(jié)構(gòu)實現(xiàn)。

1.2 注意點

①拆紅包:二倍均值算法

二倍均值算法:每次拆分后塞進子紅包的金額 = 隨機區(qū)間(0, (剩余紅包金額M / 未被搶的剩余紅包個數(shù)N) * 2)

  • 保證被拆紅包金額的差距不會太大。不會出現(xiàn)一個100元紅包,拆分為20個,一個紅包里就包含99元的情況

②發(fā)紅包:list

記錄紅包被拆分為了多少份,并且每份里有多少錢

③搶紅包&記錄:hset

記錄用戶與被搶紅包的對應關系,防止多搶

2 代碼實現(xiàn)

為了大家能看得清晰,這里我直接將所有代碼都放在了main.go,實際使用和實現(xiàn)還是應該拆分為service、controller…

2.1 拆紅包splitRedPacket

// 拆紅包
func splitRedPacket(totalMoney, totalNum int) []int {
	//1. 將紅包拆分為幾個
	redpackets := make([]int, totalNum)
	usedMoney := 0
	for i := 0; i < totalNum; i++ {
		//最后一個紅包,還剩余多少就分多少
		if i == totalNum-1 {
			redpackets[i] = totalMoney - usedMoney
		} else {
			//二倍均值算法:每次拆分后塞進子紅包的金額 = 隨機區(qū)間(0, (剩余紅包金額M / 未被搶的剩余紅包個數(shù)N) * 2)
			avgMoney := ((totalMoney - usedMoney) / (totalNum - i)) * 2
			money := 1 + rand.Intn(avgMoney-1)
			redpackets[i] = money
			usedMoney += money
		}
	}
	return redpackets
}

2.2 發(fā)紅包sendRedPacket

// 發(fā)紅包 http://localhost:9090/send?totalMoney=100&totalNum=3
func sendRedPacket(c *context2.Context) {
	money, _ := c.URLParamInt("totalMoney")
	totalNum, _ := c.URLParamInt("totalNum")
	redPackets := splitRedPacket(money, totalNum)
	uuid, _ := uuid.NewUUID()
	k := RED_PACKGE_KEY + uuid.String()
	for _, r := range redPackets {
		_, err := RedisCli.LPush(context.TODO(), k, r).Result()
		if err != nil && err != redis.Nil {
			panic(err)
		}
	}
	c.JSON(fmt.Sprintf("send redpacket[%s] succ %v", k, redPackets))
}

2.3 搶紅包&記錄robRedPacket

// 搶紅包 http://localhost:9090/rob?redPacket=e3e71f56-e9a3-11ee-9ad5-7a2cb90a4104&uId=4
func robRedPacket(c *context2.Context) {
	//判斷是否搶過
	redPacket := c.URLParam("redPacket")
	uId, _ := c.URLParamInt("uId")
	exists, err := RedisCli.HExists(context.TODO(), RED_PACKAGE_CONSUME_KEY+redPacket, fmt.Sprintf("%d", uId)).Result()
	if err != nil && err != redis.Nil {
		panic(err)
	}
	if exists {
		//表明已經(jīng)搶過
		c.JSON(fmt.Sprintf("[%d] you have already rob", uId))
		return
	} else if !exists {
		//從list里取出一個紅包
		result, err := RedisCli.LPop(context.TODO(), RED_PACKGE_KEY+redPacket).Result()
		if err == redis.Nil {
			//紅包已經(jīng)搶完了
			c.JSON(fmt.Sprintf("redpacket is empty"))
			return
		}
		if err != nil {
			panic(err)
		}
		fmt.Printf("%d rob the red packet %v\n", uId, result)
		//記錄:后續(xù)可以異步進MySQL或者MQ做統(tǒng)計分析,每一年搶了多少紅包,金額是多少【年度總結(jié)】
		_, err = RedisCli.HSet(context.TODO(), RED_PACKAGE_CONSUME_KEY+redPacket, uId, result).Result()
		if err != nil && err != redis.Nil {
			panic(err)
		}
		c.JSON(fmt.Sprintf("[%d] rob the red packet %v", uId, result))
	}

}

2.4 分析(紅包被誰搶了)infoRedPacket

func infoRedPacket(c *context2.Context) {
	redPacket := c.URLParam("redPacket")
	infoMap, err := RedisCli.HGetAll(context.TODO(), RED_PACKAGE_CONSUME_KEY+redPacket).Result()
	if err != nil && err != redis.Nil {
		panic(err)
	}
	c.JSON(infoMap)
}

全部代碼

Github:
https://github.com/ziyifast/ziyifast-code_instruction/tree/main/redis_demo/redpacket_demo

package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/google/uuid"
	"github.com/kataras/iris/v12"
	context2 "github.com/kataras/iris/v12/context"
	"math/rand"
	"time"
)

/*
通過redis實現(xiàn)迷你版微信搶紅包
1. 發(fā)紅包
2. 拆紅包(一個紅包拆分成多少個,每個紅包里有多少錢)=》二倍均值算法,將拆分后的紅包通過list放入redis
3. 搶紅包(用戶搶紅包,并記錄哪個用戶搶了多少錢,防止重復搶):hset記錄每個紅包被哪些用戶搶了
*/
var (
	RedisCli                *redis.Client
	RED_PACKGE_KEY          = "redpackage:"
	RED_PACKAGE_CONSUME_KEY = "redpackage:consume:"
)

func init() {
	rand.Seed(time.Now().UnixNano())
	RedisCli = redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
		DB:   0,
	})
}

func main() {
	app := iris.New()
	app.Get("/send", sendRedPacket)
	app.Get("/rob", robRedPacket)
	app.Get("/info", infoRedPacket)
	app.Listen(":9090", nil)
}

// 發(fā)紅包 http://localhost:9090/send?totalMoney=100&totalNum=3
func sendRedPacket(c *context2.Context) {
	money, _ := c.URLParamInt("totalMoney")
	totalNum, _ := c.URLParamInt("totalNum")
	redPackets := splitRedPacket(money, totalNum)
	uuid, _ := uuid.NewUUID()
	k := RED_PACKGE_KEY + uuid.String()
	for _, r := range redPackets {
		_, err := RedisCli.LPush(context.TODO(), k, r).Result()
		if err != nil && err != redis.Nil {
			panic(err)
		}
	}
	c.JSON(fmt.Sprintf("send redpacket[%s] succ %v", k, redPackets))
}

// 搶紅包 http://localhost:9090/rob?redPacket=e3e71f56-e9a3-11ee-9ad5-7a2cb90a4104&uId=4
func robRedPacket(c *context2.Context) {
	//判斷是否搶過
	redPacket := c.URLParam("redPacket")
	uId, _ := c.URLParamInt("uId")
	exists, err := RedisCli.HExists(context.TODO(), RED_PACKAGE_CONSUME_KEY+redPacket, fmt.Sprintf("%d", uId)).Result()
	if err != nil && err != redis.Nil {
		panic(err)
	}
	if exists {
		//表明已經(jīng)搶過
		c.JSON(fmt.Sprintf("[%d] you have already rob", uId))
		return
	} else if !exists {
		//從list里取出一個紅包
		result, err := RedisCli.LPop(context.TODO(), RED_PACKGE_KEY+redPacket).Result()
		if err == redis.Nil {
			//紅包已經(jīng)搶完了
			c.JSON(fmt.Sprintf("redpacket is empty"))
			return
		}
		if err != nil {
			panic(err)
		}
		fmt.Printf("%d rob the red packet %v\n", uId, result)
		//記錄:后續(xù)可以異步進MySQL或者MQ做統(tǒng)計分析,每一年搶了多少紅包,金額是多少【年度總結(jié)】
		_, err = RedisCli.HSet(context.TODO(), RED_PACKAGE_CONSUME_KEY+redPacket, uId, result).Result()
		if err != nil && err != redis.Nil {
			panic(err)
		}
		c.JSON(fmt.Sprintf("[%d] rob the red packet %v", uId, result))
	}

}

func infoRedPacket(c *context2.Context) {
	redPacket := c.URLParam("redPacket")
	infoMap, err := RedisCli.HGetAll(context.TODO(), RED_PACKAGE_CONSUME_KEY+redPacket).Result()
	if err != nil && err != redis.Nil {
		panic(err)
	}
	c.JSON(infoMap)
}

// 拆紅包
func splitRedPacket(totalMoney, totalNum int) []int {
	//1. 將紅包拆分為幾個
	redpackets := make([]int, totalNum)
	usedMoney := 0
	for i := 0; i < totalNum; i++ {
		//最后一個紅包,還剩余多少就分多少
		if i == totalNum-1 {
			redpackets[i] = totalMoney - usedMoney
		} else {
			//二倍均值算法:每次拆分后塞進子紅包的金額 = 隨機區(qū)間(0, (剩余紅包金額M / 未被搶的剩余紅包個數(shù)N) * 2)
			avgMoney := ((totalMoney - usedMoney) / (totalNum - i)) * 2
			money := 1 + rand.Intn(avgMoney-1)
			redpackets[i] = money
			usedMoney += money
		}
	}
	return redpackets
}

演示

1.啟動程序,調(diào)用send接口發(fā)紅包??,假設100元,拆分為3個

http://localhost:9090/send?totalMoney=100&totalNum=3

在這里插入圖片描述

2.調(diào)用rob接口搶紅包

http://localhost:9090/rob?redPacket=b246f0cc-e9a6-11ee-a234-7a2cb90a4104&uId=1

在這里插入圖片描述

此時如果用戶1再搶,應當報錯(redis已經(jīng)有記錄該用戶已搶):

在這里插入圖片描述

3.繼續(xù)調(diào)用rob接口,用戶2、用戶3搶紅包:

在這里插入圖片描述

在這里插入圖片描述

Redis中記錄:

在這里插入圖片描述

4.此時紅包??已經(jīng)被搶完了,如果有用戶4再來搶,應該返回來晚了,紅包被搶完了

在這里插入圖片描述

到此這篇關于Redis迷你版微信搶紅包實戰(zhàn)的文章就介紹到這了,更多相關Redis 微信搶紅包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 使用SpringBoot集成redis的方法

    使用SpringBoot集成redis的方法

    這篇文章主要介紹了SpringBoot集成redis的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 單線程Redis快的4 個原因總結(jié)

    單線程Redis快的4 個原因總結(jié)

    作為內(nèi)存中數(shù)據(jù)存儲,Redis 以其速度和性能著稱,通常被用作大多數(shù)后端服務的緩存解決方案,但是,在內(nèi)部,Redis 采用單線程架構(gòu),為什么單線程設計依然會有這么高的性能,在本文中,讓我們深入探討為什么 Redis 才有單線程架構(gòu)
    2023-07-07
  • Redis客戶端連接遠程Redis服務器方式

    Redis客戶端連接遠程Redis服務器方式

    這篇文章主要介紹了Redis客戶端連接遠程Redis服務器方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • redis-cli 使用密碼登錄的實例

    redis-cli 使用密碼登錄的實例

    今天小編就為大家分享一篇redis-cli 使用密碼登錄的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Redis Sentinel實現(xiàn)哨兵模式搭建小結(jié)

    Redis Sentinel實現(xiàn)哨兵模式搭建小結(jié)

    這篇文章主要介紹了Redis Sentinel實現(xiàn)哨兵模式搭建小結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • React中immutable的使用

    React中immutable的使用

    這篇文章主要介紹了React中immutable的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • Redis TTL命令實現(xiàn)數(shù)據(jù)生存時間

    Redis TTL命令實現(xiàn)數(shù)據(jù)生存時間

    生存時間可以通過Redis中的不同命令來設置、查看和管理,TTL命令是其中之一,本文主要介紹了Redis TTL命令實現(xiàn)數(shù)據(jù)生存時間,具有一定的參考價值,感興趣的可以了解一下
    2024-06-06
  • Redis緩存-序列化對象存儲亂碼問題的解決

    Redis緩存-序列化對象存儲亂碼問題的解決

    這篇文章主要介紹了Redis緩存-序列化對象存儲亂碼問題的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Redis集群詳解

    Redis集群詳解

    這篇文章主要介紹了Redis集群詳解,需要的朋友可以參考下
    2020-07-07
  • 聊一聊redis奇葩數(shù)據(jù)類型與集群知識

    聊一聊redis奇葩數(shù)據(jù)類型與集群知識

    現(xiàn)在越來越多的項目都會利用到redis,多實例redis服務比單實例要復雜的多,這里面涉及到定位、容錯、擴容等技術問題,下面這篇文章主要給大家介紹了關于redis奇葩數(shù)據(jù)類型與集群知識的相關資料,需要的朋友可以參考下
    2022-01-01

最新評論

木兰县| 晋江市| 南部县| 临海市| 桃江县| 循化| 铁岭县| 昆山市| 广水市| 鄂托克旗| 福鼎市| 临清市| 泌阳县| 满洲里市| 兴安盟| 西乡县| 三门县| 新和县| 玛多县| 大荔县| 乐陵市| 新密市| 平潭县| 塔城市| 滕州市| 尼玛县| 太康县| 仙桃市| 神木县| 邯郸市| 新昌县| 江陵县| 乌拉特前旗| 玛多县| 曲阜市| 密山市| 建阳市| 绥阳县| 湾仔区| 土默特右旗| 侯马市|