zoukankan      html  css  js  c++  java
  • 延时取消订单还在用定时任务?来看mq的实现

    电商网站中通常会有这样的需求,订单创建后,会给用户两小时用于支付,如果超时未支付,则要自动取消订单。最容易想到的实现思路就是用定时任务的方式,每分钟(或者更短的时间)在数据库中查询一次未支付的订单,检查距离订单创建是否超过两小时,如果超过,则把订单取消。这种方式在数据库繁忙时会增加数据库的压力,我们可以使用mq更优雅的实现这个需求。

    一、 rocketmq的实现

    利用rocketmq的延时消息可以很方便的实现这类需求,下面在电商项目中的模拟实现。(这里为了方便测试,把消息的延迟时间设置为1分钟)。

     

    1.写一个方法,发送一条包含订单号的消息,设置消息的延迟时间是1分钟,即消费者1分钟后才会收到这条消息。

     private void sendDelayMsg(String topic, Long orderId) throws Exception {
            MQEntity entity=new MQEntity();
            entity.setOrderId(orderId);
            Message message=new Message(topic,tag, orderId.toString(), JSON.toJSONString(entity).getBytes());
            //延迟1分钟发出
            //messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
            message.setDelayTimeLevel(5);
            DefaultMQProducer producer = mqTemplate.getProducer();
            producer.setSendMsgTimeout(20000);
            producer.setVipChannelEnabled(false);
            producer.send(message);
            log.info("发送了一条延时消息,orderId:"+orderId);
        }

    2.在确认订单后,调用发送延迟消息的方法,消息的topic是在配置文件中配置的,之后消息监听器就需要监听同样topic的消息。

    public Result confirmOrder(TradeOrder order) {
            //1.校验订单
            checkOrder(order);
            //2.生成预订单
            savePreOrder(order);
            try {
                //3.扣减库存
                reduceGoodsNum(order);
                //4.扣减优惠券
                updateCouponStatus(order.getCouponId(),order.getOrderId());
                //5.使用余额
                reduceMoneyPaid(order);
                //6.确认订单
                updateOrderStatus(order);
    ​
                //发送一个延时消息用来定时取消订单
                sendDelayMsg(delayTopic,order.getOrderId());
                //7.返回成功状态
                return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
            } catch (Exception e) {
                }
        }

    3.创建一个消息监听器,接收上一步发送的延迟消息,取到订单Id,去数据库里查询,判断订单是不是尚未支付,如果未支付,则取消订单。

    @Slf4j
    @Component
    @RocketMQMessageListener(topic = "${mq.delay.topic}",consumerGroup = "${mq.order.consumer.group.name}",
            messageModel = MessageModel.BROADCASTING)
    public class DelayMsgListener implements RocketMQListener<MessageExt> {
        @Autowired
        private TradeOrderMapper orderMapper;
        @Override
        public void onMessage(MessageExt messageExt) {
            log.info("接收到延迟消息成功");
            String body=new String(messageExt.getBody());
            MQEntity entity = JSON.parseObject(body, MQEntity.class);
            Long orderId = entity.getOrderId();
            TradeOrder order = orderMapper.selectById(orderId);
            //检查订单状态是否是未支付
            if(!ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY.equals(order.getPayStatus())){
                order.setOrderStatus(ShopCode.SHOP_ORDER_CANCEL.getCode());
                orderMapper.updateById(order);
                log.info("取消订单成功,orderId:"+orderId);
            }
    ​
        }
    }

    4.用单元测试方法,测试订单的延时取消

    @Test
        public void confirmOrder() throws IOException {
            TradeOrder order=new TradeOrder();
            order.setUserId(345963634385633280l);
            order.setCouponId(365959443973935104l);
            order.setCouponPaid(new BigDecimal(50));
            order.setGoodsId(345959443973935104l);
            order.setGoodsNumber(1);
            order.setGoodsPrice(new BigDecimal(5000));
            order.setGoodsAmount(new BigDecimal(5000));
            order.setShippingFee(BigDecimal.ZERO);
            order.setOrderAmount(new BigDecimal(5000));
            order.setMoneyPaid(new BigDecimal(100));
            orderService.confirmOrder(order);
    ​
            System.in.read();
    ​
        }

    5.执行日志如下,可以看到在14:07发送了延迟消息,在14:08分监听器收到了延迟消息,并且​做了取消订单的操作。至此,此需求就实现了​。

    二、rabbitmq的实现思路

    rabbitmq中没有延迟消息,但可以为消息设置存活时间,当消息超过了存活时间,则被放到某个死信队列中,创建一个交换机专门用来处理这个死信队列中的消息​,就可以实现同样的功能。

     

    三、其它的类似需求

    1. 用户注册会员发放的优惠券于一周后失效

    2. 优惠活动持续三天后取消

    这种在某个事件发生的一段时间后,要进行的操作,都可以用mq来实现。

  • 相关阅读:
    java反射笔记
    Java常见异常类型
    找了这么多毕业设计题目,反而不知道选什么了
    C#中Trim()、TrimStart()、TrimEnd()的用法
    JS bom对象
    HTML随笔
    Sublim text3汉化
    11G RAC ORA-32701
    DB_LINK
    ORA-16957: SQL Analyze time limit interrupt
  • 原文地址:https://www.cnblogs.com/wsw-tcsygrwfqd/p/15057846.html
Copyright © 2011-2022 走看看