抢单通过redis布隆过滤器解决高并发
抢单两个比较麻烦的问题:
高并发的问题。超卖的问题。
实现过程分为四层:
1.流量截断层。通过开始标识,开始时间做秒杀的开始判断操作,截取一定流量
2.流量拦截。通过设置M个秒杀商品放入在redis中,然后当有M*1.2个用户进来抢则停止后面用户进来继续商品的抢购。
3.信息校验业务逻辑层。这层才是真正的让用户抢到商品,通过lua文件处理用户是否已经抢到商品。
4.通过队列将 订单存入到DB中。
Controller层
@RestController
public class SeckillController {
@Resource
private SeckillService seckillService;
@RequestMapping("/redis/seckill")
public String secKill(int uid,int skuId){
return seckillService.seckill(uid,skuId);
}
}
service 层:
@Service
public class SeckillService {
private static final String secStartPrefix = "skuId_start_";
private static final String secAccess = "skuId_access_";
private static final String secCount = "skuId_count_";
private static final String filterName = "skuId_bloomfilter_";
private static final String bookedName = "skuId_booked_";
@Resource
private RedisService redisService;
public String seckill(int uid, int skuId) {
//流量拦截层
//1、判断秒杀是否开始 0_1554045087 开始标识_开始时间
String isStart = (String) redisService.get(secStartPrefix + skuId);
if (StringUtils.isBlank(isStart)) {
return "还未开始";
}
if (isStart.contains("_")) {
Integer isStartInt = Integer.parseInt(isStart.split("_")[0]);
Integer startTime = Integer.parseInt(isStart.split("_")[1]);
if (isStartInt == 0) {
if (startTime > getNow()) {
return "还未开始";
} else {
//代表秒杀已经开始
redisService.set(secStartPrefix + skuId, 1+"");
}
} else {
return "系统异常";
}
} else {
if (Integer.parseInt(isStart) != 1) {
return "系统异常";
}
}
//流量拦截
String skuIdAccessName = secAccess + skuId;
Integer accessNumInt = 0;
String accessNum = (String) redisService.get(skuIdAccessName);
if(StringUtils.isNotBlank(accessNum)){
accessNumInt = Integer.parseInt(accessNum);
}
String skuIdCountName = secCount + skuId;
Integer countNumInt = Integer.parseInt((String) redisService.get(skuIdCountName));
if (countNumInt * 1.2 < accessNumInt) {
return "抢购已经完成,欢迎下次参与";
} else {
redisService.incr(skuIdAccessName);
}
//信息校验层
if (redisService.bloomFilterExists(filterName, uid)){
return "您已经抢购过该商品,请勿重复下发!";
}else{
redisService.bloomFilterAdd(filterName, uid);
}
//通过lua脚本可以实现命令的原子性操作
Boolean isSuccess = redisService.getAndIncrLua(bookedName+skuId);
if(isSuccess){
return "恭喜您抢购成功!!!";
}else{
return "抢购结束,欢迎下次参与";
}
}
private long getNow() {
return System.currentTimeMillis() / 1000;
}
}
lau文件脚本的使用保证命令操作的原子性,商品库存扣除时防止超卖的情况。
local lockKey = KEYS[1]
-- get info
local result_1 = redis.call('GET', lockKey)
if tonumber(result_1) <10000
then
local result_2= redis.call('INCR', lockKey)
return result_1
else
return result_1
end
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/80416.html