[gulimall] 秒杀

秒杀流程

1. 合法性校验

  1. 秒杀时间校验:当前时间是否在秒杀时间范围内
  2. 接口幂等性校验:验证这个人是否已经买过了,去redis里使用setnx命令占位,过期时间=秒杀截止时间-当前时间

2. 获取信号量

  1. 信号量为秒杀的商品数
  2. 使用信号量的tryacquire方法实现分布式锁

3. 秒杀成功

  1. 发送订单信息到MQ,异步下单
  2. 直接返回秒杀成功结果,节省用户等待时间

4. 限流措施

  1. 前端限流,一些高并发的网站直接在前端页面开始限流,例如:小米的验证码设计
  2. nginx限流,直接负载部分请求到错误的静态页面:令牌算法 漏斗算法
  3. 网关限流,限流的过滤器。或者使用专业的限流组件sentinel
  4. 代码中使用分布式信号量
  5. rabbitmq限流(能者多劳:chanel.basicQos(1)),保证发挥所有服务器的性能。

代码

    @Override
    public String kill(String killId, Integer num) throws InterruptedException {

        long s1 = System.currentTimeMillis();
        //获取当前用户的信息
        MemberResponseVo user = LoginUserInterceptor.loginUser.get();

        //1、获取当前秒杀商品的详细信息从Redis中获取
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(SECKILL_CHARE_PREFIX);
        String skuInfoValue = hashOps.get(killId);
        if (StringUtils.isEmpty(skuInfoValue)) {
            return null;
        }
        //(合法性效验)
        SeckillSkuRedisTo redisTo = JSON.parseObject(skuInfoValue, SeckillSkuRedisTo.class);
        Long startTime = redisTo.getStartTime();
        Long endTime = redisTo.getEndTime();
        long currentTime = System.currentTimeMillis();
        //判断当前这个秒杀请求是否在活动时间区间内(效验时间的合法性)
        if (currentTime < startTime || currentTime > endTime) {
        	return null;
        }
        //3、验证购物数量是否合理和库存量是否充足
        Integer seckillLimit = redisTo.getSeckillLimit();
        //获取信号量
        String seckillCount = redisTemplate.opsForValue().get(SKU_STOCK_SEMAPHORE + randomCode);
        Integer count = Integer.valueOf(seckillCount);
        //判断信号量是否大于0,并且买的数量不能超过库存
        if (count > 0 && num <= seckillLimit && count > num ) {
            //4、验证这个人是否已经买过了(幂等性处理),如果秒杀成功,就去占位。userId-sessionId-skuId
            //SETNX 原子性处理
            String redisKey = user.getId() + "-" + skuId;
            //设置自动过期(活动结束时间-当前时间)
            Long ttl = endTime - currentTime;
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
            if (aBoolean) {
                //占位成功说明从来没有买过,分布式锁(获取信号量-1)
                RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + randomCode);
                //TODO 秒杀成功,快速下单
                boolean semaphoreCount = semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
                //保证Redis中还有商品库存
                if (semaphoreCount) {
                    //创建订单号和订单信息发送给MQ
                    // 秒杀成功 快速下单 发送消息到 MQ 整个操作时间在 10ms 左右
                    String timeId = IdWorker.getTimeId();
                    SeckillOrderTo orderTo = new SeckillOrderTo();
                    orderTo.setOrderSn(timeId);
                    orderTo.setMemberId(user.getId());
                    orderTo.setNum(num);
                    orderTo.setPromotionSessionId(redisTo.getPromotionSessionId());
                    orderTo.setSkuId(redisTo.getSkuId());
                    orderTo.setSeckillPrice(redisTo.getSeckillPrice());
                    rabbitTemplate.convertAndSend("order-event-exchange","order.seckill.order",orderTo);
                    return timeId;
                }
            }
        }
        return null;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值