RabbitMQ实现下单超时自动取消支付

问题场景

在淘宝或者京东下单,订单创建成功,等待支付,一般会给30分钟的时间,开始倒计时。如果在这段时间内用户没有支付,则默认订单取消,该如何实现?

  1. 定期轮询(数据库等) 用户下单成功,将订单信息放入数据库,同时将支付状态放入数据库,用户付款更改数据库状态。定期轮询数据库支付状态,如果超过30分钟就将该订单取消。
    优点: 设计实现简单
    缺点: 需要对数据库进行大量的IO操作,效率低下。
  2. 基于Timer
    缺点: Timers没有持久化机制.,Timers不灵活 (只可以设置开始时间和重复间隔,对等待支付貌似够用),Timers 不能利用线程池,一个timer一个线程,Timers没有真正的管理计划
  3. ScheduledExecutorService
    优点: 可以多线程执行,一定程度上避免任务间互相影响,单个任务异常不影响其它任务。
    缺点: 在高并发的情况下,不建议使用定时任务去做,因为太浪费服务器性能
  4. RabbitMQ,使用TTL,消息过期投递到死信队列

实现原理

用户下单,将订单存放到交换机ex.order(过期时间为下单超时时间30分钟),消息到q_order队列中,不设置该队列的消费者(故此消息一直未消费).,同时指定一个死信交换机ex.order.dlx,并绑定一个死信队列q.order.dlx,当消息超过30分钟过期变成死信时,该消就会被发送到该死信队列上,由死信消费者消费,判断订单id是否支付,如果未支付则修改为支付超时订单过期。

代码

声明订单和死信队列和交换机,并绑定,(订单消息过期时间这里为了方便测试,设置为10s)

@Configuration
public class RabbitConfig {
    @Bean
    public Queue queue() {
        Map<String, Object> props = new HashMap<>();
        // 消息的生存时间 10s
        props.put("x-message-ttl", 10000);
        // 设置该队列所关联的死信交换器(当队列消息到期后依然没有消费,则加入死信 队列)
        props.put("x-dead-letter-exchange", "ex.order.dlx");
        // 设置该队列所关联的死信交换器的routingKey,如果没有特殊指定,使用原队列的routingKey
        props.put("x-dead-letter-routing-key", "order.dlx");
        Queue queue = new Queue("q.order", true, false, false, props);
        return queue;
    }
    @Bean
    public Queue queueDlx() {
        Queue queue = new Queue("q.order.dlx", true, false, false);
        return queue;
    }
    @Bean
    public Exchange exchange() {
        DirectExchange exchange = new DirectExchange("ex.order", true,false, null);
        return exchange;
    }
    /**
     * 死信交换器
     * @return
     */
    @Bean
    public Exchange exchangeDlx() {
        DirectExchange exchange = new DirectExchange("ex.order.dlx", true,false, null);
        return exchange;
    }
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(exchange()).with("order").noargs();
    }

    @Bean
    public Binding bindingDlx() {
        return BindingBuilder.bind(queueDlx()).to(exchangeDlx()).with("order.dlx").noargs();
    }
}

订单contoller

@RestController
public class OrderController {

    @Autowired
    private AmqpTemplate rabbitTemplate;
    @Autowired
    private OrderInfoMapper orderInfoMapper;


    /**
     * 提交订单
     * @return
     */
    @RequestMapping("/submit")
    public Integer submitOrder() {
		//添加订单信息到mysql
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setStatus(0);
        orderInfoMapper.insertSelective(orderInfo);
		//将消息发送到order队列
        rabbitTemplate.convertAndSend("ex.order", "order", orderInfo.getId());

        return orderInfo.getId();
    }

    /**
     * 支付
     * @return
     */
    @RequestMapping("/pay/{id}")
    public Integer pay(@PathVariable Integer id) {

        OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(id);

        //订单过期
        if(orderInfo.getStatus().equals(2)){
            //跳转用户历史账单中查看因付款超时而取消的订单
            return 2;
        }else{
            orderInfo.setStatus(1);
            orderInfoMapper.updateByPrimaryKey(orderInfo);
            //跳转到付款成功页面
            return 1;

        }
    }

    @RequestMapping("/orderList")
    public List<OrderInfo> orderList() {

        List<OrderInfo> orderInfos = orderInfoMapper.selectAll();

        return orderInfos;
    }

}

死信消费者监听q.order.dlx死信队列,消费过期消息

@Component
public class TimeoutOrderListener {

    @Autowired
    OrderInfoMapper orderInfoMapper;

    @RabbitListener(queues = "q.order.dlx")
    public void onMessage(String orderId)  {
		//通过从死信队列中获取的订单号,查询订单
        OrderInfo orderInfo = orderInfoMapper.selectByPrimaryKey(Integer.valueOf(orderId));

        //判断订单状态如果为未支付,则修改状态为过期
        if(orderInfo.getStatus().equals(0)) {
            orderInfo.setStatus(2);
            orderInfoMapper.updateByPrimaryKey(orderInfo);
        }

    }

}

效果演示

提交订单
在这里插入图片描述
页面跳转至支付
在这里插入图片描述
观察订单信息进入到订单队列
在这里插入图片描述
10s之后由于一直没有点支付,订单队列中的消息一直未被消费,消息过期,进入到q.order.dlx死信队列,死信队列消费者消费到消息,修改订单状态为过期(由于消费者直接监听到进行消费,所以可能看不到死信队列消息)
在这里插入图片描述
此时点击支付后,由于订单已经过期,直接跳转至过期
在这里插入图片描述

代码地址

https://github.com/Nicococococo/rabbitmq-order

  • 16
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
使用RabbitMQ实现订单下单支付场景中,可以通过以下步骤来实现: 1. 用户下单后,订单系统将订单信息落库,并将订单信息发送到RabbitMQ的消息队列中。 2. 支付系统监听RabbitMQ的消息队列,一旦接收到订单信息,开始处理支付逻辑。 3. 支付系统根据订单信息进行支付操作,如果支付成功,则更新订单支付状态为已支付,并发送支付成功的回调消息到RabbitMQ的消息队列中。 4. 订单系统监听支付成功的回调消息,一旦接收到支付成功的回调消息,更新订单状态为已支付。 5. 如果支付系统在一定时间内收到支付成功的回调消息,则认为支付失败,订单系统可以根据需要进行相应的处理,例如关闭订单或释放库存。 通过使用RabbitMQ的消息队列订单系统和支付系统可以解耦,实现异步处理,提高系统的可靠性和稳定性。同时,可以根据实际需求设置合适的超时时间和重试机制,以确保订单支付的准确性和及时性。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [RabbitMQ实现订单超时设计思路、以及在订单过期临界点支付成功如何处理](https://blog.csdn.net/hkl_Forever/article/details/128216981)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [RabbitMQ的应用场景](https://blog.csdn.net/weixin_54721305/article/details/123668635)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值