rabbitmq系列(四)死信队列

简介: 当消息在一个队列中变成一个死信之后,它将被重新publish到另一个交换机上,这个交换机我们就叫做死信交换机,死信交换机将死信投递到一个队列上就是死信队列。

一、什么是死信队列

当消息在一个队列中变成一个死信之后,它将被重新publish到另一个交换机上,这个交换机我们就叫做死信交换机,死信交换机将死信投递到一个队列上就是死信队列。具体原理如下图:

死信交换机.png

消息变成死信的三种情况:

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false
  • 消息TTL过期
  • 队列达到最大长度

二、手动签收应答模式

应答模式分为两种,手动签收和自动签收,自动应答就是消费者消费了一条消息就自动告诉队列删除消息。这样的弊端就是不管消费逻辑有没有成功,都会将消息删除,这样就会造成消息丢失。而使用手动签收后,就是在消费逻辑处理成功后,手动告诉队列消费成功,然后队列再去删除这条消息。

  1. 再消费者配置文件中开启手动签收模式
spring.rabbitmq.listener.simple.acknowledge-mode = manual
  1. 在消费逻辑处理成功后手动签收,修改消费者代码
@RabbitListener(queues = QUEUE_NAME)
    public void receiveMessage(Message message,@Headers Map<String,Object> headers, Channel channel) throws Exception {

        Jedis jedis = new Jedis("localhost", 6379);

        String messageId = message.getMessageProperties().getMessageId();
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("接收导的消息为:"+msg+"==消息id为:"+messageId);

        String messageIdRedis = jedis.get("messageId");

        if(messageId == messageIdRedis){
            return;
        }
        JSONObject jsonObject = JSONObject.parseObject(msg);
        String email = jsonObject.getString("email");
        String content = jsonObject.getString("timestamp");

        String httpUrl = "http://127.0.0.1:8080/email?email"+email+"&content="+content;
        // 如果发生异常则返回null
        String body = HttpUtils.httpGet(httpUrl, "utf-8");
        //
        if(body == null){
            throw new Exception();
        }
        jedis.set("messageId",messageId);
        Long deliveryTag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
        // 手动签收
        channel.basicAck(deliveryTag,false);
    }

三、产生死信的三种情况演示

  • 消息被拒绝

我们继续修改一下消费者代码,尝试让消费者消费的时候发生异常。然后在catch块中拒绝消息。

// 拒绝消息,给死信队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);

我们运行程序后发现,当消息消费异常后在队列”zb-byte1“中的消息被消费了,同时发现在死信队列”dead-byte-zb“中有一条未被消费的消息。消息到死信队列后,然后我们在创建一个消费者去消费消息就可以了。当然死信队列也需要去手动签收消息。

  • 消息TTL过期

这种模式我们也叫做延迟消费,有一种特别经典的案例就是用户在一个商品抢购系统中,用户抢到商品后需要在30分钟时间内支付,不然订单无效。这时候我们就可以通过消息TTL过期来实现,设置队列消息过期时间为30分钟,30分钟后publish到死信队列,我们在死信队列中消费订单状态是否支付成功来判断该订单是否有效。

非常简单,我们只需要在配置死信交换机的时候设置有效时间就可以了

  @Bean
    public Queue queue(){

        Map<String,Object> map = new HashMap<>();
        map.put("x-dead-letter-exchange",BEI_EXCHANGE_NAME);
        map.put("x-dead-letter-routing-key",BEI_ROUTING_KEY);
        map.put("x-message-ttl",7200); // 队列过期时间
        Queue queue = new Queue(QUEUE_NAME,true,false,false,map);
        return queue;
    }
  • 队列达到最大长度

设置队列长度即可:

 @Bean
    public Queue queue(){

        Map<String,Object> map = new HashMap<>();
        map.put("x-dead-letter-exchange",BEI_EXCHANGE_NAME);
        map.put("x-dead-letter-routing-key",BEI_ROUTING_KEY);
        map.put("x-max-length",3);
//        map.put("x-message-ttl",7200); // 队列过期时间
        Queue queue = new Queue(QUEUE_NAME,true,false,false,map);
        return queue;
    }

设置好之后,我们先不要启动消费者,然后调用生成者往队列中发送消息,当消息长度大于3时,我们发现消息进入了死信队列。

注意:前文中也提到过,队列不能被修改,也就是说已经创建好的队列设置了过期时常为7200s,然后我们注释掉,增加队列长度是3的代码,这样运行会报错,必须在rabbitmq中将该队列删除,然后重新生成队列才可以。

相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
3月前
|
消息中间件 监控 Java
Spring Boot中的RabbitMQ死信队列魔法:从异常到延迟,一网打尽【RabbitMQ实战 一】
Spring Boot中的RabbitMQ死信队列魔法:从异常到延迟,一网打尽【RabbitMQ实战 一】
86 0
|
2月前
|
消息中间件 存储 NoSQL
RabbitMQ的幂等性、优先级队列和惰性队列
**摘要:** 本文讨论了RabbitMQ中的幂等性、优先级队列和惰性队列。幂等性确保了重复请求不会导致副作用,关键在于消费端的幂等性保障,如使用唯一ID和Redis的原子性操作。优先级队列适用于处理不同重要性消息,如大客户订单优先处理,通过设置`x-max-priority`属性实现。惰性队列自3.6.0版起提供,用于延迟将消息加载到内存,适合大量消息存储和消费者延迟消费的场景。
27 4
|
24天前
|
消息中间件 Java API
RabbitMQ入门指南(五):Java声明队列、交换机以及绑定
RabbitMQ是一个高效、可靠的开源消息队列系统,广泛用于软件开发、数据传输、微服务等领域。本文主要介绍了Java声明队列、交换机以及绑定队列和交换机等内容。
30 0
|
1月前
|
消息中间件 Java Maven
springboot 使用注解的方式创建rabbitmq的交换机、路由key、以及监听队列的名称
springboot 使用注解的方式创建rabbitmq的交换机、路由key、以及监听队列的名称
|
1月前
|
消息中间件 存储 NoSQL
rocketmq实现延迟队列思路探讨
本文介绍了两种实现RocketMQ延迟消息的方法。非任意时间延迟可通过在服务器端配置`messageDelayLevel`实现,但需重启服务。任意时间延迟则分为两种策略:一是结合原生逻辑和时间轮,利用RocketMQ的默认延迟等级组合支持任意延迟,但可能丢失1分钟内的数据;二是使用存储介质(如Redis)加时间轮,消息存储和定时发送结合,能处理数据不一致和丢失问题,但涉及更多组件。推荐项目[civism-rocket](https://github.com/civism/civism-rocket)作为参考。
69 1
|
2月前
|
消息中间件 前端开发 算法
【十七】RabbitMQ基础篇(延迟队列和死信队列实战)
【十七】RabbitMQ基础篇(延迟队列和死信队列实战)
45 1
|
3月前
|
消息中间件 监控 数据挖掘
兔子的后院奇遇:深入了解RabbitMQ中的死信队列【RabbitMQ 四】
兔子的后院奇遇:深入了解RabbitMQ中的死信队列【RabbitMQ 四】
51 0
|
3月前
|
消息中间件 Docker 容器
docker构建rabbitmq并配置延迟队列插件
docker构建rabbitmq并配置延迟队列插件
42 0
|
3月前
|
消息中间件
rabbitmq动态创建队列
rabbitmq动态创建队列
39 0
|
2月前
|
消息中间件 网络协议 JavaScript
MQTT常见问题之微消息队列mqtt支持ipv6失败如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总: