场景:获取库存,判断是不是抢到商品的人特别的多。
读多写少:redis。
订单校验:一个用户只能买一个商品。
放在消息队列:订单的写队列。多线程监听,异步入库。
返回用户:订单校验通过之后返回给用户,抢单成功。
---------------------------------------------------------------------------------10-7-------------------------------------------------
网关:动态数据的流量拦截。
----------------------------------------------------------------------------10-8------------------------------------------------
秒杀实战:第二层,网关流量的拦截
1.判断秒杀是不是开始
2.流量拦截
-----
第一步:
/**
*
* @param uid 当前的用户的id
* @param skuId 商品的id
* @return
*/
@RequestMapping("/redis/seckill")
public String secKill(int uid,int skuId){
return seckillService.seckill(uid,skuId);
}
第二步:
redis里面存了什么?
// 秒杀开始的标志
private static final String secStartPrefix = "skuId_start_";
// 已经秒杀商品的数量
private static final String secAccess = "skuId_access_";
// 秒杀商品的总数量
private static final String secCount = "skuId_count_";
// 校验一个人是不是下单成功了bookedName+uid
private static final String filterName = "skuId_bloomfilter_";
// 解决超卖的问题
private static final String bookedName = "skuId_booked_";
public String seckill(int uid, int skuId) {
//流量拦截层
//1、判断秒杀是否开始 0_1554045087 开始标识_开始时间
String isStart = (String) redisService.get(secStartPrefix + skuId);
if (StringUtils.isBlank(isStart)) {
return "还未开始";
}
// 秒杀开始了 拿到标识0是未开始
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 "系统异常";
}
}
// 为1就不走上面的步骤 直接开始秒杀了
// 开始秒杀要进行流量拦截 流量拦截
// 已经秒杀的商品的数量
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 {
// 已经秒杀商品的数量+1
redisService.incr(skuIdAccessName);
}
//信息校验层
if (redisService.bloomFilterExists(filterName, uid)){
return "您已经抢购过该商品,请勿重复下发!";
}else{
redisService.bloomFilterAdd(filterName, uid);
}
Boolean isSuccess = redisService.getAndIncrLua(bookedName+skuId);
if(isSuccess){
return "恭喜您抢购成功!!!";
}else{
return "抢购结束,欢迎下次参与";
}
}
---------------------------------------------------------------------------------10-9------------------------------------------------
布隆过滤器:
一个用户不能抢到两次。
用户的id不能存到内存里面,会挂掉的。
存在redis。就是布隆过滤器。
//信息校验层
if (redisService.bloomFilterExists(filterName, uid)){
return "您已经抢购过该商品,请勿重复下发!";
}else{
redisService.bloomFilterAdd(filterName, uid);
}
---------------------------------------------------------------------------------10-10-----------------------------------------------
库存扣减的功能。流量拦截不用lua是追求的高性能的,放入13000也是可以的。
控制超卖必须用lua脚本。
第一步:lua脚本
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
第二步:防止超卖
Boolean isSuccess = redisService.getAndIncrLua(bookedName+skuId);
if(isSuccess){
return "恭喜您抢购成功!!!";
}else{
return "抢购结束,欢迎下次参与";
}
-------------------------------------------------------------------------10-11-----------------------------------------------
秒杀的网址:https://blog.csdn.net/shijinghan1126/article/details/87952339