分布式Day02-秒杀场景

1、redis 解决超发问题:使用redis缓存商品信息,进行商品预售,生产预售订单。

       (1)开启一个定时任务,定时读取商品信息,把秒杀商品按照对应的数量以队列的形式缓存到redis 中。

    @Scheduled(cron = "0/5 * * * * ?")
    public void startSecKill(){
        List list  = promotionSecKillDAO.findUnstartSecKill();
        for(PromotionSecKill ps : list){
            System.out.println(ps.getPsId() + "秒杀活动已启动");
            //删掉以前重复的活动任务缓存
            redisTemplate.delete("seckill:count:" + ps.getPsId());
            //有几个库存商品,则初始化几个list对象
            for(int i = 0 ; i < ps.getPsCount() ; i++){
                redisTemplate.opsForList().rightPush("seckill:count:" + ps.getPsId() , ps.getGoodsId());
            }
            ps.setStatus(1);
            promotionSecKillDAO.update(ps);
        }
    }

       (2)用户秒杀商品:从缓存队列里pop出商品,同时将用户信息加入到缓存订单队列里(将订单信息推送到mq中,进行高并发场景下限流)。生产商品订单信息并返回给用户。 若无异常,则表示用户抢到商品,进行下一步操作(支付支付接口、快递接口)... 生成订单。

public void processSecKill(Long psId, String userid, Integer num) throws SecKillException {
        PromotionSecKill ps = promotionSecKillDAO.findById(psId);
        if (ps == null) {
            //秒杀活动不存在
            throw new SecKillException("秒杀活动不存在");
        }
        if (ps.getStatus() == 0) {
            throw new SecKillException("秒杀活动还未开始");
        } else if (ps.getStatus() == 2) {
            throw new SecKillException("秒杀活动已经结束");
        }
        Integer goodsId = (Integer) redisTemplate.opsForList().leftPop("seckill:count:" + ps.getPsId());
        if (goodsId != null) {
            //判断是否已经抢购过
            boolean isExisted = redisTemplate.opsForSet().isMember("seckill:users:" + ps.getPsId(), userid);
            if (!isExisted) {
                System.out.println("恭喜您,抢到商品啦。快去下单吧");
                redisTemplate.opsForSet().add("seckill:users:" + ps.getPsId(), userid);
            }else{
                redisTemplate.opsForList().rightPush("seckill:count:" + ps.getPsId(), ps.getGoodsId());
                throw new SecKillException("抱歉,您已经参加过此活动,请勿重复抢购!");
            }
        } else {
            throw new SecKillException("抱歉,该商品已被抢光,下次再来吧!!");
        }


    }

       (3)开启一个定时任务,更新缓存以及数据库中所有商品的状态。

    @Scheduled(cron = "0/5 * * * * ?")
    public void endSecKill(){
        List psList = promotionSecKillDAO.findExpireSecKill();
        for (PromotionSecKill ps : psList) {
            System.out.println(ps.getPsId() + "秒杀活动已结束");
            ps.setStatus(2);
            promotionSecKillDAO.update(ps);
            redisTemplate.delete("seckill:count:" + ps.getPsId());
        }
    }

2、使用消息队列(rabbitmq)进行订单流浪削峰,代码片段:

@RabbitListener(
        bindings = @QueueBinding(
                value = @Queue(value = "queue-order") ,
                exchange = @Exchange(value = "exchange-order" , type = "fanout")
        )
)
@RabbitHandler
public void handleMessage(@Payload Map data , Channel channel ,
                          @Headers Map headers){
    System.out.println("=======获取到订单数据:" + data + "===========);");

    try {
        //对接支付宝、对接物流系统、日志登记。。。。
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Order order = new Order();
        order.setOrderNo(data.get("orderNo").toString());
        order.setOrderStatus(0);
        order.setUserid(data.get("userid").toString());
        order.setRecvName("xxx");
        order.setRecvMobile("1393310xxxx");
        order.setRecvAddress("xxxxxxxxxx");
        order.setAmout(19.8f);
        order.setPostage(0f);
        order.setCreateTime(new Date());
        orderDAO.insert(order);
        Long tag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
        channel.basicAck(tag , false);//消息确认
        System.out.println(data.get("orderNo") + "订单已创建");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

你可能感兴趣的:(java,分布式,缓存,redis)