本篇介绍基于Redis 的高并发抢红包设计和示例分析
1 流程结构
红包入池
1.1 抢红包
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 总结
从数据库中取出红包数据,用redis list存储(LPUSH),
红包总数用redis Hash 存储(hincrBy)
每个用户申请的红包数用redis Hash存储(HSET)
用户申请红包先判断领取数量,当可以申请红包时,从红包列表中弹出一个红包数据,
对返回的红包数据进行处理
用户领取的红包自增1 redis (hincrBy)
清场后,根据红包剩余数量 redis (llen) 和红包总数计算领取的红包数,
更新红包场次状态和红包统计信息redis (hget)