由于项目需求,研究年会抽红包功能的实现方法,具体实现方法如下:
实现功能:1、PC端大屏幕点击开始抽奖,移动客户端摇一摇,抽红包;
2、分多轮抽奖,每一轮抽奖数量事先设定
3、奖品丰富不一样
实现逻辑:1、将抽奖批次与数量放入redis缓存当中,并创建奖品信息队列;
2、当客户端开始摇一摇时,从redis缓存中获取抽奖批次,判断数量是否大于0,并加锁,获取奖品信息,循环15次,每次先休眠300毫秒;
经压测,可承受10万次的并发
具体实现伪代码如下:
1、将抽奖批次放入内存中:
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxActive(-1); // 控制一个pool可分配多少个jedis实例
config.setMaxIdle(3000); // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例
config.setTestOnBorrow(true); // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
config.setMaxWait(30000); // 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出 JedisConnectionException
JedisPool jedisPool = new JedisPool(config, IP地址,端口号);
Jedis jedis = jedisPool.getResource();
//先删除旧的批次
String oldMgpbId = jedis.get("批次标识");
if(StringUtils.isNotBlank(oldMgpbId)){
jedis.del(oldMgpbId );
}
jedis.del("批次标识");
jedis.del("奖品队列标识");
//将抽奖批次与数量放入内存中
jedis.set("批次标识", "本轮奖品数量");
//返还到连接池
jedisPool.returnResource(jedis);
//奖品信息集合
List<String> prizes = new ArrayList<String>();
/**********创建奖品队列***********/
jedis = jedisPool.getResource();
//将奖品信息打乱放入队列中
int prizeNum = 本轮奖品数量;
for (int j = prizeNum; j > 0; j--) {
Random random = new Random();
int num = random.nextInt(j);
//将奖品信息放入队列中,并从list中移除
String prize = prizes.remove(num);
jedis.lpush("奖品队列标识", prize);
}
//返还到连接池
jedisPool.returnResource(jedis);
2、当PC端的大屏幕点击开始抽奖的时候,设置本批次抽奖时间:
//将抽奖批次的主键放入内存中
jedis.set("批次标识", "本批次主键");
//设置失效时间
jedis.expire("批次标识", 失效时间);
3、移动客户端开始摇奖:
int m = 0;
while(m < 15){ //循环15次
try {
//休眠300毫秒
Thread.currentThread().sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取抽奖批次主键
String mgpbId = jedis.get("批次标识");
if(StringUtils.isBlank(mgpbId)){ //若为空,说明抽奖批次不存在或结束
logger.info("本轮抽奖已结束");
break;
}
if(jedis.setnx("随意参数", "随意参数") == 1){ //加锁成功
//设置失效时间
jedis.expire("随意参数", 2);
//获取本批次抽奖数量
int num = Integer.parseInt(jedis.get(mgpbId));
if(num > 0){ //若数量大于0,则表示中奖了
//抽奖数量-1
jedis.set(mgpbId, (num -1) + "");
//释放锁
jedis.del("随意参数");
/**************新增中奖信息************/
//从奖品信息队列中获取奖品信息
List<String> prizeList = jedis.brpop(0, "奖品队列标识");
if(CollectionUtils.isEmpty(prizeList)){
break;
}
//取第二个元素
String prizeInfo = prizeList.get(1);
//解析 prizeInfo 奖品信息,返回信息提示用户中奖
break;
} else if(num == 0){ //若为0,则清空本批次抽奖
jedis.del("批次标识");
jedis.del("本批次主键");
//释放锁
jedis.del("随意参数");
break;
}
}
m++;
}
jedisPool.returnResource(jedis);
其实主要是利用redis的锁机制以及队列,关于redis,有兴趣的同学可以动手自己搭一个试试。