Redis实现抢红包

文章介绍了在高并发环境下实现抢红包功能的技术方案,包括使用Redis的List结构和Hash结构确保并发下的原子性,以及通过二倍均值法实现红包分配算法,保证每个人只能抢一次且防止作弊。同时,文章讨论了并发控制和库存管理的难点,如是否加锁以及如何确保数据一致性。
摘要由CSDN通过智能技术生成

1、问题分析

发红包lpush redPacket001 10 20 30 20 10【List结构】
抢红包

高并发

多线程

不加锁

原子性

lpop redpacket

记录红包

盘点+汇总 防止作弊 一个人只能操作一次 【Hash结构】

hset redpacket cid1 10 cid2 20

红包分发算法二倍均值法

2、难点分析:

1、红包分配算法,平均or随机?

2、次数限制,每个人只能抢一次

3、没抢走一次,红包个数就少一个,确定库存。。。是否加锁,保证原子性?

抢红包:

1、抢到,不能加锁且原子性,支持高并发

2、每人只能有一次抢到机会,记录抢到金额

分发算法:

业内常用算法,二倍均值法

n个人抢m元红包,抢到的人分配的金额  [0,m/n*2] 内随机

3、实现

1、抢红包算法

private Integer[] splitRedPackageAlgorithm(int totalMoney,int redPackageNumber)
    {
        Integer[] redPackageNumbers = new Integer[redPackageNumber];
        //已经被抢夺的红包金额,已经被拆分塞进子红包的金额
        int useMoney = 0;

        for (int i = 0; i < redPackageNumber; i++)
        {
            if(i == redPackageNumber - 1)
            {
                redPackageNumbers[i] = totalMoney - useMoney;
            }else{
                //二倍均值算法,每次拆分后塞进子红包的金额 = 随机区间(0,(剩余红包金额M ÷ 未被抢的剩余红包个数N) * 2)
                int avgMoney = ((totalMoney - useMoney) / (redPackageNumber - i)) * 2;
                redPackageNumbers[i] = 1 + new Random().nextInt(avgMoney - 1);
            }
            useMoney = useMoney + redPackageNumbers[i];
        }
        return redPackageNumbers;
    }

2、发红包

public String sendRedPackage(int totalMoney,int redPackageNumber)
    {
        //1 拆红包,将总金额totalMoney拆分为redPackageNumber个子红包
        Integer[] splitRedPackages = splitRedPackageAlgorithm(totalMoney,redPackageNumber);//拆分红包算法通过后获得的多个子红包数组
        //2 发红包并保存进list结构里面且设置过期时间
        String key = RED_PACKAGE_KEY+IdUtil.simpleUUID();
        redisTemplate.opsForList().leftPushAll(key,splitRedPackages);
        redisTemplate.expire(key,1, TimeUnit.DAYS);

        //3 发红包OK,返回前台显示
        return key+"\t"+ Ints.asList(Arrays.stream(splitRedPackages).mapToInt(Integer::valueOf).toArray());
    }

3、抢红包

public String robRedPackage(String redPackageKey,String userId)
    {
        //1 验证某个用户是否抢过红包,不可以多抢
        Object redPackage = redisTemplate.opsForHash().get(RED_PACKAGE_CONSUME_KEY + redPackageKey, userId);
        //2 没有抢过可以去抢红包,否则返回-2表示该用户抢过红包了
        if(null == redPackage)
        {
            //2.1 从大红包(list)里面出队一个作为该客户抢的红包,抢到了一个红包
            Object partRedPackage = redisTemplate.opsForList().leftPop(RED_PACKAGE_KEY + redPackageKey);
            if(partRedPackage != null)
            {
                //2.2 抢到红包后需要记录进入hash结构,表示谁抢到了多少钱的某个子红包
                redisTemplate.opsForHash().put(RED_PACKAGE_CONSUME_KEY+redPackageKey,userId,partRedPackage);
                System.out.println("用户:"+userId+"\t 抢到了多少钱的红包:"+partRedPackage);
                //TODO 后续异步进mysql或者MQ进一步做统计处理,每一年你发出多少红包,抢到了多少红包,年度总结
                return String.valueOf(partRedPackage);
            }
            // 抢完了
            return "errorCode:-1,红包抢完了";
        }
        //3 某个用户抢过了,不可以作弊抢多次
        return "errorCode:-2,   message:"+userId+"\t"+"你已经抢过红包了,不能重新抢";
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值