抢单通过redis布隆过滤器解决高并发

抢单通过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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值