主要的并发是在抢红包和拆红包的。
-------------------------------------------------------------------------------9-1--------------------------------------------------------------------------------------
数据库建表。
如何使用generater生成代码?
第一步:配置和引入
第二步:
第三步:
数据库表的设计:
CREATE TABLE `red_packet_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`red_packet_id` bigint(11) NOT NULL DEFAULT 0 COMMENT '红包id,采用timestamp+5位随机数',
`total_amount` int(11) NOT NULL DEFAULT 0 COMMENT '红包总金额,单位分',
`total_packet` int(11) NOT NULL DEFAULT 0 COMMENT '红包总个数',
`remaining_amount` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包金额,单位分',
`remaining_packet` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包个数',
`uid` int(20) NOT NULL DEFAULT 0 COMMENT '新建红包用户的用户标识',
`create_time` timestamp COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='红包信息表,新建一个红包插入一条记录';
CREATE TABLE `red_packet_record` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` int(11) NOT NULL DEFAULT '0' COMMENT '抢到红包的金额',
`nick_name` varchar(32) NOT NULL DEFAULT '0' COMMENT '抢到红包的用户的用户名',
`img_url` varchar(255) NOT NULL DEFAULT '0' COMMENT '抢到红包的用户的头像',
`uid` int(20) NOT NULL DEFAULT '0' COMMENT '抢到红包用户的用户标识',
`red_packet_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '红包id,采用timestamp+5位随机数',
`create_time` timestamp COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='抢红包记录表,抢一个红包插入一条记录';
--------------------------------------------------------------------------9-2--------------------------------------------------------------------------------
发红包接口的实现。
实现的思路:
抢到红包之后不一定能拆开红包。
代码:新增红包。
/***
*
* @param uid
* @param totalNum
* @return
*/
@ResponseBody
@RequestMapping("/addPacket")
public String saveRedPacket(Integer uid, Integer totalNum, Integer totalAmount) {
RedPacketInfo record = new RedPacketInfo();
record.setUid(uid);
record.setTotalAmount(totalAmount);
record.setTotalPacket(totalNum);
record.setCreateTime(new Date());
record.setRemainingAmount(totalAmount);
record.setRemainingPacket(totalNum);
record.setUpdateTime(new Date());
long redPacketId = System.currentTimeMillis(); //此时无法保证红包id唯一,最好是用雪花算法进行生成分布式系统唯一键
record.setRedPacketId(redPacketId);
redPacketInfoMapper.insert(record);
// 在redis存入红包的总个数 和红包的总金额
redisService.set(redPacketId + "_totalNum", totalNum + "");
redisService.set(redPacketId + "_totalAmount", totalAmount + "");
return "success";
}
新增红包:数据库加一条红包记录,把红包的数量红包的金额和剩余红包的数量剩余红包的金额存入数据库。
redis的储存:剩余红包的数量,剩余红包的金额。
controller里面的成员变量:https://www.zhihu.com/question/271297000
代码:抢红包
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean decr(final String key, int value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.increment(key,-value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 抢红包
* @return
*/
@ResponseBody
@RequestMapping("/addScores")
public String getRedPacket(Integer uid, long redPackId) {
RedPacketRecord redPacketRecord = new RedPacketRecord();
redPacketRecord.setUid(uid);
redPacketRecord.setRedPacketId(redPackId);
redPacketRecord.setAmount(11111);
redPacketRecord.setCreateTime(new Date());
redPacketRecordMapper.insertSelective(redPacketRecord);
redisService.decr(redPackId+"_totalAmount",1);
return "success";
}
补充:实际上我们还要判断这个人是不是重复抢了红包的。
测试:http://localhost:8080/addPacket?uid=123&totalNum=10&totalAmount=100
--------------------------------------------------------------9-3--------------------------------------------------------------------------------
抢红包:
判断的逻辑:
抢红包:
/**
* 抢红包
*实际开发要校验用户是不是存在的
* @param redPacketId
* @return
*/
@ResponseBody
@RequestMapping("/getPacket")
public Integer getRedPacket(String redPacketId) {
String redPacketName = redPacketId + TOTAL_NUM;
String num = (String) redisService.get(redPacketName);
if (StringUtils.isNotBlank(num)) {
return Integer.parseInt(num);
}
return 0;
}
测试:http://localhost:8080/getPacket?redPacketId=1581997706758
抢红包:不对redis和db做任何操作,只是返回有没有红包,有了就抢到,但是不一定可以领到钱。
--------------------------------------------------------------9-4--------------------------------------------------------------------------------
抢到红包不一定能领到。
/**
* 拆红包
*
* @param redPacketId
* @return
*/
@ResponseBody
@RequestMapping("/getRedPacketMoney")
public String getRedPacketMoney(int uid, long redPacketId) {
Integer randomAmount = 0;
// 存的是剩余的红包的数量
String redPacketName = redPacketId + TOTAL_NUM;
//存的是剩余红包的金额
String totalAmountName = redPacketId + TOTAL_AMOUNT;
// 红包的个数
String num = (String) redisService.get(redPacketName);
if (StringUtils.isBlank(num) || Integer.parseInt(num) == 0) {
return "抱歉!红包已经抢完了";
}
String totalAmount = (String) redisService.get(totalAmountName);
// 抢到的金额
if (StringUtils.isNotBlank(totalAmount)) {
Integer totalAmountInt = Integer.parseInt(totalAmount);
Integer totalNumInt = Integer.parseInt(num);
Integer maxMoney = totalAmountInt / totalNumInt * 2;
Random random = new Random();
randomAmount = random.nextInt(maxMoney);
}
//课堂作业:lua脚本将这两个命令一起请求
redisService.decr(redPacketName, 1);
redisService.decr(totalAmountName,randomAmount); //redis decreby功能
// 更新红包流水
updateRacketInDB(uid, redPacketId,randomAmount);
return randomAmount + "";
}
public void updateRacketInDB(int uid, long redPacketId, int amount) {
RedPacketRecord redPacketRecord = new RedPacketRecord();
redPacketRecord.setUid(uid);
redPacketRecord.setRedPacketId(redPacketId);
redPacketRecord.setAmount(1111);
redPacketRecord.setCreateTime(new Date());
redPacketRecordMapper.insertSelective(redPacketRecord);
//这里应该查出RedPacketInfo的数量,将总数量和总金额减去
}
--------------------------------------------------------------9-5--------------------------------------------------------------------------------
项目总结:
第一个问题:入账的时候,判断下数据库和redis的金额是不是一样的。
第二个问题:用户拆红包是不是限制下?可以用消息队列做一个只放进来40个用户假设有20个用户。
第三个问题:redis的红包数量和金额的操作。
这里的并发量用lua脚本。保证在同一次命令。可能进行了第一步之后第二步就有很多线程进来了。
第四个问题:
---------------------------------------------------------------------------9-6---------------------------------------------------------------------------------------