Hexo

点滴积累 豁达处之

0%

记redis抢红包设计实例

本篇介绍基于Redis 的高并发抢红包设计和示例分析

1 流程结构

红包入池

1.1 抢红包

redis红包

2 实例

2.1 红包池操作类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface AfRedPacketPoolService {

//注入红包
public void inject(Collection<String> packets);

//申请一个红包
public String apply();

//清空红包池
public void emptyPacket();

//红包池状态信息
public Map<String,Integer> informationPacket();

// 红包对象数据
static class Redpacket{
//。。。
}

对应实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Service("redPacketRedisPoolService")
public class AfRedPacketRedisPoolServiceImpl implements AfRedPacketPoolService {

@Resource
private BizCacheUtil bizCacheUtil;

@PostConstruct
public void init(){
}

@Override
public void inject(Collection<String> packets) {
bizCacheUtil.lpush(Constants.CACHEKEY_REDRAIN_SINK, packets); // 红包入池
bizCacheUtil.hincrBy(Constants.CACHEKEY_REDRAIN_MISC, "sumPacketsCurRound", (long)packets.size()); //红包总数
}

@Override
public String apply() {
int queueSum = bizCacheUtil.keys(Constants.CACHEKEY_REDRAIN_SINK).size(); //红包剩个数
if(queueSum == 0) {
return null;
}

Object o = bizCacheUtil.rpop(Constants.CACHEKEY_REDRAIN_SINK); // 弹出一个红包
if(o == null) return null;
return o.toString();
}

@Override
public void emptyPacket() { //清空红包
bizCacheUtil.delCache(Constants.CACHEKEY_REDRAIN_SINK);
bizCacheUtil.delCache(Constants.CACHEKEY_REDRAIN_MISC);
}

@Override
public Map<String,Integer> informationPacket() {
int surplus = (int) bizCacheUtil.llen(Constants.CACHEKEY_REDRAIN_SINK); //红包剩余个数

Map<String,Integer> map = new HashMap<String,Integer>();
map.put("listNumber",surplus);

//红包总数
String info = bizCacheUtil.hget(Constants.CACHEKEY_REDRAIN_MISC, "sumPacketsCurRound");
if(StringUtil.isEmpty(info)){
info= "0";
}
map.put("count", Integer.valueOf(info));

return map;
}

}

2.2 准备红包数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void scanAndInjected() {
try {
//注入
HashSet<String> sink = new HashSet<>(8096); //红包数据列表
List<AfRedRainPoolDto> pools = afRedRainPoolDao.queryAll(); //从数据库查询红包列表
int num;
Integer rewardType;
for (AfRedRainPoolDto pool : pools) {
num = pool.getNum();
for (int i = 0; i < num; i++) {
// 添加红包数据
sink.add(JSON.toJSONString(new Redpacket(round.getId())));
}
}
if (CollectionUtil.isNotEmpty(pools)) {
redPacketRedisPoolService.inject(sink); //红包入池
}

} catch (Exception e) {
redPacketRedisPoolService.emptyPacket(); //清空红包池
}
}

2.3 客户端申请红包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public Redpacket apply(String userName) {
Assert.hasText(userName);
Integer counter;
synchronized (userName.intern()) { //防刷
//获取用户命中红包数量
String val = this.bizCacheUtil.hget(Constants.CACHEKEY_REDRAIN_COUNTERS, userName);
if (val == null) {
this.bizCacheUtil.hset(Constants.CACHEKEY_REDRAIN_COUNTERS, userName, "0");
} else {
counter = Integer.valueOf(val);
}

//判断用户领取数量
if (++counter > MAX_NUM_HIT_REDPACKET) {
return null;
}

//申请红包 返回红包数据
String rpStr = this.redPacketRedisPoolService.apply();
if (rpStr != null) {
Redpacket rp = JSON.parseObject(rpStr, Redpacket.class);

//处理返回的红包数据
AfUserDo user = afUserService.getUserByUserName(userName);
if (RewardType.COIN.getCode().equals(rp.getRewardType())) {
afTaskUserService.addCoinTaskUser(user.getRid(), "红包雨奖励");
}
//更新用户领取数量
this.bizCacheUtil.hincrBy(Constants.CACHEKEY_REDRAIN_COUNTERS, userName, 1L);
return rp;
}
}
return null;
}

3 总结

  1. 从数据库中取出红包数据,用redis list存储(LPUSH),

    红包总数用redis Hash 存储(hincrBy)

  2. 每个用户申请的红包数用redis Hash存储(HSET)

  3. 用户申请红包先判断领取数量,当可以申请红包时,从红包列表中弹出一个红包数据,

  4. 对返回的红包数据进行处理

  5. 用户领取的红包自增1 redis (hincrBy)

  6. 清场后,根据红包剩余数量 redis (llen) 和红包总数计算领取的红包数,

    更新红包场次状态和红包统计信息redis (hget)