RabbitMQ使用死信队列解决订单超时

1.什么是死信队列(DLX)?

DLX,Dead Letter Exchange 的缩写,又死信邮箱、死信交换机。DLX就是一个普通的交换机,和一般的交换机没有任何区别。
当消息在一个队列中变成死信(dead message)时,通过这个交换机将死信发送到死信队列中

死信的条件:

  1. 消息被否定确认,使用 channel.basicNackchannel.basicReject ,并且此时requeue 属性被设置为false。
  2. 消息在队列的存活时间超过设置的TTL时间。
  3. 消息队列的消息数量已经超过最大队列长度。

2.使用死信队列来解决订单超时未支付

我的环境是springboot2.3.0

首先明确一下大致思路:

  1. 用户下单后,发送消息(带有TTL过期时间)到一个普通队列,然后这个普通队列设置了死信交换机路由
  2. 到达过期时间后,消息成为死信,由死信交换机转发到死信队列
  3. 消费者监听死信队列,处理过期订单

大概的模型如下:
在这里插入图片描述

2.1配置队列和交换机

我的配置文件信息:

	#死信消息模型
    dead:
      exchange: seckill.dead.exchange
      routing-key: seckill.dead.routingKey
      normal-queue: seckill.dead.normal.queue
      normal-exchange: seckill.dead.normal.exchange
      normal-routing-key: seckill.dead.normal.routingKey
      real-dead-queue: seckill.dead.queue
    #设置过期时间
    expire: 1800000

先配置指定了死信交换机和死信路由的队列,这里容易搞混,这里的指定死信交换机和路由,并不是绑定,只是指定了在这个队列里成为死信的消息会发送到指定的死信交换机里,然后死信交换机会分发给真正的用于存储死信的队列

	/** 普通的队列,但是指定了死信交换机和路由,在这个队列中的消息过期后会由死信交换机分发到真正的死信队列 */
    @Bean
    public Queue normalQueue(){
        Map<String, Object> args = new HashMap<>();
        //设置死信参数
        //指定死信交换机
        args.put("x-dead-letter-exchange",myRabbitProperties.getDead().getExchange());
        //指定死信路由
        args.put("x-dead-letter-routing-key",myRabbitProperties.getDead().getRoutingKey());
        return new Queue(myRabbitProperties.getDead().getNormalQueue(),true,false,false,args);
    }

配置普通交换机,并绑定普通交换机和普通队列

	/** 创建普通交换机 */
    @Bean
    public Exchange normalExchange(){
        return new TopicExchange(myRabbitProperties.getDead().getNormalExchange(),true,false);
    }

    /** 绑定普通队列和普通交换机 */
    @Bean
    public Binding normalBind(){
        return BindingBuilder.bind(normalQueue()).to(normalExchange()).with(myRabbitProperties.getDead().getNormalRoutingKey()).noargs();
    }

配置死信交换机和真实用来存储死信的队列,并绑定真实存储死信的队列和死信交换机

	/** 创建死信交换机 */
    @Bean
    public Exchange deadExchange(){
        return new TopicExchange(myRabbitProperties.getDead().getExchange(),true,false);
    }

    /** 创建真实存储死信的队列,当死信队列中消息过期后,转发到此队列,真实存储死信队列需要绑定死信交换机和路由 */
    @Bean
    public Queue realDeadQueue(){
        return new Queue(myRabbitProperties.getDead().getRealDeadQueue(),true,false,false);
    }

    /** 绑定真实存储死信的队列与死信交换机 */
    @Bean
    public Binding realBindDead(){
        return BindingBuilder.bind(realDeadQueue()).to(deadExchange()).with(myRabbitProperties.getDead().getRoutingKey()).noargs();
    }
2.2 在服务中写具体发送业务,也就是生产者
	public void sendDeadMsg(String orderId) {
        log.info("开始发送死信消息!");
        try {
            SecOrderAndUserInfo orderAndUserInfo = orderMapper.selectOrderAndUserByOrderId(orderId);
            if (orderAndUserInfo!=null) {
                rabbitTemplate.convertAndSend(myRabbitProperties.getDead().getNormalExchange(), myRabbitProperties.getDead().getNormalRoutingKey(), orderAndUserInfo, new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        MessageProperties messageProperties = message.getMessageProperties();
                        //设置过期时间TTL
                        messageProperties.setExpiration(String.valueOf(myRabbitProperties.getExpire()));
                        //设置持久化
                        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                        return message;
                    }
                });
            }
        } catch (Exception e) {
            log.error("死信消息发送异常,消息为:{}", orderId);
        }
    }
2.3 消费者监听存储死信的队列
 	/**
     * 监听死信队列
     * @param orderAndUserInfo 收到的消息
     */
    @RabbitListener(queues = "${mq.myrabbit.dead.real-dead-queue}",containerFactory = "singleListenerContainer")
    public void receiveDeadMsg(SecOrderAndUserInfo orderAndUserInfo){
        try {
            log.info("死信队列接收到消息:{}",orderAndUserInfo);
            if (orderAndUserInfo!=null){
                SecOrder order = orderMapper.selectOrderByOrderId(orderAndUserInfo.getOrderId());
                //订单存在,并且订单状态处于未处理状态
                if (order!=null&&orderAndUserInfo.getState()== MyproCostant.ORDER_NO_OPERATION){
                    //将订单状态设置为已过期
                    order.setState(MyproCostant.ORDER_EXPIRED);
                    orderMapper.updateOrder(order);
                }
            }
        }catch (Exception e){
            log.error("死信队列接收消息异常!");
            e.printStackTrace();
        }
    }

3.进行测试

设置过期时间TTL=10秒,10秒后成功监听到了死信消息
在这里插入图片描述

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
天猫商城是一个基于SSM框架的综合性B2C电商平台,需求设计主要参考天猫商城的购物流程:用户从注册开始,到完成登录,浏览商品,加入购物车,进行下单,确认收货,评价等一系列操作。 作为模拟天猫商城系统的核心组成部分之一,采用SSM框架的天猫数据管理后台包含商品管理,订单管理,类别管理,用户管理和交易额统计等模块,实现了对整个商城的一站式管理和维护。本课程是一门专业的Java微服架构开发实战课程,主要讲解了当下流行的SpringBoot框架、SpringCloud架构以及与第三方技术整合开发实战内容。通过本课程的学习,能够理解并掌握SpringBoot的基础知识,同时能够掌握SpringBoot与常用的第三方技术整合实现实际开发中的业务需求,包括实现Web开发、数据访问、缓存管理、安全管理、消息服务、任务管理等;了解并掌握SpringCloud微服务架构的基础知识及相关组件的应用,掌握微服务架构在企业级开发的实践,建立起微服架构思想。项目技术栈:采用SpringBoot简化商城系统的初始搭建以及开发过程采用SpringMVC+Spring+IBatis完成项目的整合采用Mysql作为数据库存储,Druid配置数据库连接池采用SpringCloud+Netflix 微服务技术栈的实战开发使用Redis完成缓存的数据存储,搭建Redis搭建主从、哨兵、集群应用,保证Redis的高可用使用ElasticSearch全文检索系统进行商品数据搜索,使用ElasticSearch搭建搜索服务的高可用使用Ngnix实现页面动静分离与负载均衡的配置采用FastDFS文件储存系统文件存储,完成广告图片、商品图片的上传和存储系统使用采用CAS+shiro单点登录系统实现用户认证使用ECharts根据后台查询数据生成图表使用POI实现了商城盈利状况的Excel表格导出。商品的详情页使用Thymeleaf完成页面静态化,减少页面数据展示延迟项目中使用SpringBoot下的Aop + 自定义注解完成用户行为记录,日志采集后台管理系统使用Shiro实现登录验证和权限管理(超级管理员、管理员、产品编辑员)项目整合微信完成订单的支付使用Redission完成分布式锁,生成订单的编号使用SpringCloud Alibaba Seat完成下订单模块的分布式事务(新增订单表,库存减少,库存超卖设计)使用RabbitMQ 做消息队列,完成订单未支付自动取消和模块直接的解耦合使用Quartz任务调度,完成缓存的定时刷新,保证缓存的一致性使用本地消息表机制完成消息然队列RabbitMQ消息可靠性传输订单支付模块使用微信扫码支付,并设置订单超时自动取消通过Jquery实现前端校验,通过基于Hibernate的Valida注解实现后端的校验功能使用Base64编码对Json数据传输进行编码和解码项目使用RESTful设计风格实现资源的访问,实现前后端分离项目使用聚合数据第三方短信平台完成用户的登陆功能项目使用SpringBoot整合JavaMail完成邮件的发送项目使用SpringBoot整合Swagger2生成接口文档使用PostMan完成接口的测试项目的测试:SpringTest、dbunit、EasyMock使用Docker 进行应用的自动化打包和发布、自动化测试和持续集成、部署和调整其他应用使用 PowerDesigner,完成数据库的建模项目使用禅道进行BUG管理环境采用Maven实施多模块项目构建,采用Git进行项目版本管理 架构解读:  项目部分截图:              讲义部分截图:          

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值