微信红包设计流程讲解与实战分析

#1024程序员节 | 征文#

前言

微信红包作为大家耳熟能详的一种互动方式,其背后的技术支持包含多个方面。从用户发出红包到红包被抢完,涉及到的流程包括发红包、红包存储、红包拆分以及抢红包等。本文将详细介绍这一系列流程,并通过代码案例来实践讲解,特别重点分析红包的拆分算法。

微信红包设计流程

整个红包流程按照发红包、红包拆分、抢红包的顺序来设计。在数据结构的选择上,考虑到抢红包的高并发特性和即时响应要求,采用Redis非关系数据库进行设计是优于MySQL的。Redis的每个命令都是单线程执行,保证原子性操作,无需额外的锁机制。

1. 发红包

一个红包通常会被拆分成多个小红包,例如100元可以拆分为20元、20元、20元、30元和10元。这种情况下,可以使用Redis的list结构来存储这些拆分后的小红包。

2. 抢红包

在高并发环境下,如何保证多线程抢红包时不加锁且保持原子性是关键。Redis的特性使得每个命令都是单线程且原子性的,因此使用LPOP命令即可实现。

3. 记红包

为了确保同一个用户不能抢夺同一个红包两次,需要记录哪些红包被哪些用户抢过。这里可以使用Redis的hash结构来进行存储。

拆红包算法

在拆红包算法中,较为合理的是采用“二倍均值算法”。该算法的核心思想是每次拆分红包时,取一个随机区间,其最大值为剩余红包金额的两倍与未被抢的剩余红包个数的乘积。这样可以保证拆分的随机性和公平性。

代码实现

二倍均值算法的具体代码实现如下:

private Integer[] splitRedPackageAlgorithm(int totalMoney, int redPackageNumber){

    Integer[] splitRedPackageNumbers = new Integer[redPackageNumber];

    int useMoney = 0;

    for (int i = 0; i < redPackageNumber; i++) {

        if(i == redPackageNumber - 1){

            splitRedPackageNumbers[i] = totalMoney - useMoney;

        }else {

            int avgMoney = ((totalMoney - useMoney) / (redPackageNumber - i)) \* 2;

            splitRedPackageNumbers[i] = 1 + new Random().nextInt(avgMoney - 1);

        }

        useMoney += splitRedPackageNumbers[i];

    }

    return splitRedPackageNumbers;

}

为什么生成每个小红包金额使用如下代码随机生成?

splitRedPackageNumbers[i] = 1 + new Random().nextInt(avgMoney - 1);

首先,前面加1的原因是new Random().nextInt(avgMoney -1)会生成0到avgMoney - 1之间的随机数,但不能为0。为了保证第一个红包不为0元,所以需要+1。其次,avgMoney - 1是为了控制随机数的最大值接近于avgMoney但不等于它。这样可以确保最后一个红包也不会为0元。如果不这样做,有可能每次随机数都取最大值,导致最后剩余的红包金额只能为0。

后面avgMoney -1,如果不-1,结果是怎样呢?接下来模拟不-1的情况下,假设100块分5个红包,每次随机数都取最大值,那么有如下情况:

红包useMoneyavgMoney每次随机数最大本次红包金额
1020 * 2new Random().nextInt(avgMoney) == 3940
24015*2new Random().nextInt(avgMoney) == 2930
37010*2new Random().nextInt(avgMoney) == 1920
4905*2new Random().nextInt(avgMoney) == 910
500

发红包

发红包的主要步骤是将拆分后的小红包保存到Redis的list结构中,并设置过期时间。具体代码如下:

@RequestMapping(value = "/send")

public String sendRedPackage(int totalMoney, int redPackageNumber){

    Integer[] splitRedPackages = splitRedPackageAlgorithm(totalMoney,redPackageNumber);

    String key = RED\_PACKAGE\_KRY + IdUtil.simpleUUID();

    redisTemplate.opsForList().leftPushAll(key,splitRedPackages);

    redisTemplate.expire(key,1, TimeUnit.DAYS);

    return key+"\t" + Ints.asList(Arrays.stream(splitRedPackages).mapToInt(Integer::valueOf).toArray());

}

调用发红包接口,比如20块钱分成5个红包,查看redis。

抢红包

抢红包的逻辑是先验证用户是否已经抢过该红包,如果没有则允许其抢红包并记录到Redis中。具体代码如下:

@RequestMapping(value = "/rob")

public String robRedPackage(String redPackageKey,String userId){

    Object redPackage = redisTemplate.opsForHash().get(RED\_PACKAGE\_CONSUME\_KRY + redPackageKey, userId);

    if (redPackage == null){

        Object partRedPackage = redisTemplate.opsForList().leftPop(RED\_PACKAGE\_KRY + redPackageKey);

        if (partRedPackage != null){

            redisTemplate.opsForHash().put(RED\_PACKAGE\_CONSUME\_KRY+redPackageKey,userId,partRedPackage);

            System.out.println("用户"+userId+" 抢到红包了 " + partRedPackage);

            return String.valueOf(partRedPackage);

        }

        return "errorCode : -1 ,红包抢完了";

    }

    return "errorCode : -2 ," + userId + " 你已经抢过红包了";

}

调用抢红包接口后,用户可以返回抢到的红包金额,并且在Redis中也能查看到相应的红包记录。

总结

本文主要介绍了微信红包的拆分、发放和抢红包的流程,并重点讲解了二倍均值法在拆红包算法中的应用。通过随机生成红包金额的方式,实现了公平且随机的抢红包效果。整个过程中涉及到了Redis数据库的高效利用以及高并发环境下的数据处理问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小明爱吃火锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值