RabbitMQ(四) - 队列的特殊属性

一、死信队列

DLX ,全称为 Dead-Letter-Exchange ,可以称之为死信交换器,也有人称之为死信邮箱。当消息在一个队列中变成死信(dead message)之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX ,绑定 DLX 的队列就称之为死信队列。

消息变成死信,一般是由于以下几种情况:
<1>消息被拒绝(Basic.Reject/Basic.Nack),井且设置 requeue 参数为false
<2>消息过期
<3>队列达到最大长度

①绑定DLX,成为死信队列

通过在 channel.queueDeclare 方法中设置 x-dead-letter-exchange 参数来为这个队列添加 DLX。

1.1 属性解析

x-dead-letter-exchange: 死信交换器名称
x-dead-letter-routing-key:相当于转发到死信交换器所使用的路由键,不配置默认就是原队列(NORMAL_QUEUE_NAME)的路由键。举个例子,原队列的路由键是"key",配置了该属性为"key1",且假定死信路由器为Direct模式的,死信队列的绑定键需为"key1"(对应配置属性的路由键),消息成为死信后才能成功进入死信队列;相对的,若未配置该属性,依然假定死信路由器为Direct模式的,死信队列的绑定键需为"key"(即原队列的路由键),消息成为死信后才能成功进入死信队列。

1.2 代码实现

public class DLXTest {
    private static final String NORMAL_EXCHANGE_NAME = "exchange.normal";
    private static final String DLX_EXCHANGE_NAME = "exchange.dlx";
    private static final String ROUTING_KEY = "routing key";
    private static final String NORMAL_QUEUE_NAME = "queue.normal";
    private static final String DLX_QUEUE_NAME = "queue.dlx";
    private static final String IP_ADDRESS = "localhost";
    private static final int PORT = 5672;//RabbitMQ 服务端默认端口号为5672
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(IP_ADDRESS);
        factory.setPort(PORT);
        factory.setUsername("guest");
        factory.setPassword("guest");

        Connection connection = factory.newConnection();//创建连接
        Channel channel = connection.createChannel(); //创建信道

        channel.exchangeDeclare (DLX_EXCHANGE_NAME,"direct", true);
        channel.exchangeDeclare (NORMAL_EXCHANGE_NAME,"fanout", true) ;
        Map<String , Object> dlxArgs = new HashMap<String , Object>() ;
        dlxArgs.put ("x-message-ttl", 10000);
        dlxArgs.put ("x-dead-letter-exchange", DLX_EXCHANGE_NAME) ;
        dlxArgs.put ("x-dead-letter-routing-key", ROUTING_KEY);//相当于转发到死信交换器所使用的路由键,不配置默认就是原队列(NORMAL_QUEUE_NAME)的路由键
        //普通队列
        channel.queueDeclare (NORMAL_QUEUE_NAME, true , false , false , dlxArgs) ;
        channel.queueBind (NORMAL_QUEUE_NAME, NORMAL_EXCHANGE_NAME, "");
        //死信队列
        channel.queueDeclare (DLX_QUEUE_NAME, true, false , false , null) ;
        channel.queueBind (DLX_QUEUE_NAME, DLX_EXCHANGE_NAME, ROUTING_KEY ) ;
        channel.basicPublish (NORMAL_EXCHANGE_NAME, "rkey",
                MessageProperties.PERSISTENT_TEXT_PLAIN, "dlx".getBytes () ) ;

        //关闭资源
        channel.close();
        connection.close();
    }
}

②结论

队列未过期时,先存在于queue.normal,过期后自动进入死信队列。
对于 RabbitMQ 来说, LX 是一个非常有用的特性 它可以处理异常情况下,消息不能够被消费者正确消费(消费者调用了 Basic.Nack 或者 Basic.Reject )而被置入死信队列中的情况,后续分析程序可以通过消费这个死信队列中的内容来分析当时所遇到的异常情况,进而可以改善和优化系统。
在这里插入图片描述

标签解释:

D:持久化
TTL:消息超时时间
DLX:死信交换器
DLK:死信路由键

二、延迟队列

延迟队列存储的对象是对应的延迟消息,所谓“延迟消息”是指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。

①应用场景

用户希望通过手机远程遥控家里的智能设备在指定的时间进行工作。这时候就可以将用户指令发送到延迟队列,当指令设定的时间到了再将指令推送到智能设备

②实现方式

AMQP 协议中,或者 RabbitMQ 本身没有直接支持延迟队列的功能,但是可以通过前面
所介绍的 DLX TTL 模拟出延迟队列的功能。
对于 queue.dlx 这个死信队列来说,同样可以看作延迟队列。假设一个应用中需要将每条消息都设置为 10 秒的延迟,生产者通过 exchange.normal 这个交换器将发送的消息存储在 queue.normal 这个队列中。消费者订阅的并非是 queue.normal 这个队列,而是 queue.dlx 这个队列 。当消息从 queue.normal 这个队列中过期之后被存入 queue.dlx 这个队列中,消费者就恰巧消费到了延迟 10 秒的这条消息。

③具体应用

如下图,可以声明四个队列,分别设置TTL(超时时间)为5秒,10秒,30秒,60秒,其相对应的死信队列即为对应超时时间的延迟队列了。
在这里插入图片描述

三、优先级队列

优先级队列,顾名思义,具有高优先级的队列具有高的优先权,优先级高的消息具备优先被消费的特权。

①首先,需要设置队列的最大优先级

Map<String, Object> priorityArgs = new HashMap<String, Object> ();
priorityArgs.put ("x-max-priority", 10);
channel.queueDeclare ("queue.priority", true, false , false , priorityArgs) ;

控制台显示:
在这里插入图片描述

②其次,设置单一消息的优先级

这里发送四条消息,优先级分别为5、1、2、10。

AMQP.BasicProperties.Builder builder= new AMQP.BasicProperties.Builder() ;
builder.priority(5);
AMQP.BasicProperties properties = builder.build () ;
channel.basicPublish ("priorityX","rk_priority", properties, ("message1").getBytes ());

builder.priority(1);
properties = builder.build () ;
channel.basicPublish ("priorityX","rk_priority", properties, ("message2").getBytes ());

builder.priority(2);
properties = builder.build () ;
channel.basicPublish ("priorityX","rk_priority", properties, ("message3").getBytes ());

builder.priority(10);
properties = builder.build () ;
channel.basicPublish ("priorityX","rk_priority", properties, ("message4").getBytes ());

消费者消费顺序: message4(10)、message1(5)、message3(2)、message2(1)
在这里插入图片描述

③结论

优先级默认最低为0,最高为队列设置的最大优先级。优先级高的消息可以被优先消费,这个也是有前提的:如果在消费者的消费速度大于生产者的速度Broker 中没有消息堆积的情况下,对发送的消息设置优先级也就没有什么实际意义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值