未路由的消息

当生产这发送的消息到达指定的交换器后,如果交换器无法根据自身类型、绑定的队列以及消息的路由键找到匹配的队列,默认情况下消息将被丢弃。可以通过两种方式

处理这种情况,一是在发送是设置mandatory参数,二是通过备份交换器。

设置mandatory参数

在发送消息是,可以设置mandatory参数未true,这样当消息在交换器上无法被路由时,服务器将消息返回给生产者,生产者实现回调函数处理被服务端返回的消息。

public class NoRouteMessage {
private static String QUEUE = "unreachable_queue";
private static String EXCHANGE = "unreachable_exchange";
private static String BINDING_KEY = "fake_key"; public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory cf = new ConnectionFactory();
Connection connection = cf.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT,false);
channel.queueDeclare(QUEUE,false,false,false,null);
channel.queueBind(QUEUE,EXCHANGE,BINDING_KEY); String message = "an unreachable message";
boolean mandatory = true;
channel.basicPublish(EXCHANGE,"mykey",mandatory,null,message.getBytes()); channel.addReturnListener(new ReturnListener() {
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("replyCode: " + replyCode);
System.out.println("replyText: " + replyText);
System.out.println("exchange: " + exchange);
System.out.println("routingKey: " + routingKey);
System.out.println("message: " + new String(body));
}
}); try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} channel.close();
connection.close(); }
}

如上代码所示,创建了队列并和direct类型的交换器使用"fake_key"绑定,发送消息时,设定消息路由键为"mykey",这样消息到达交换器时将无比被路由。由于发送消息时

设置basicPublish的参数为true,并为生产这添加处理返回消息的回调方法,这样,消息将被服务端返回并在回调中得到处理。

备份交换器

与设置mandatory将无法路由的消息返回给生产者不同,可以为交换器设置一般备份交换器(Alternate Exchange),这样,消息在交换器上无法路由时,将被直接发送到

备份交换器,由备份交换器再次路由。

在下面到示例中,创建了交换器source_exchange,生产者将消息发送到该交换器。source_exchange并未绑定任何队列,这将导致消息被丢弃。为了处理这种情况,创建

了交换器ae并绑定了一个队列,然后将ae作为source_exchange对备份交换器,这是通过创建source_exchange交换器时设定alternate-exchange参数完成的。之后,发送到

source_exchange到消息将被服务端发送到ae交换器中,然后路由到ae_queue等待处理。

public class AlternateExchange {
private static String QUEUE = "ae_queue";
private static String EXCHANGE = "source_exchange";
private static String AE = "ae"; private static String BINDING_KEY = "fake_key"; public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory cf = new ConnectionFactory();
Connection connection = cf.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(AE, "fanout"); Map<String,Object> exArgs = new HashMap<String, Object>();
exArgs.put("alternate-exchange",AE);
channel.exchangeDeclare(EXCHANGE,"direct",false,false,exArgs); channel.queueDeclare(QUEUE,false,false,false,null); channel.queueBind(QUEUE,AE,""); channel.basicPublish(EXCHANGE,"anyKey",null,"message".getBytes()); }
}

TTL

在RabbitMQ中,可以为消息和队列设置过期时间。消息过期未被消费后,默认被丢弃;队列过期也会被删除。

消息的TTL

可以通过两种方式来为消息设置TTL,一是在发送消息是设置单条消息的TTL;二是在队列上通过队列属性设置TTL,这种情况下,路由到该队列到消息都拥有同样都TTL。

当然,也可以同时使用两种方式,这时,消息的TTL取两者中较小的。

  1. 设置单条消息都TTL

    使用basic.Publish发送消息时,通过expiration参数设置消息的TTL。

    AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
    builder.expiration("10000"); //ttl 10s
    AMQP.BasicProperties properties = builder.build();
    channel.basicPublish(EXCHANGE,"",properties,"10s TTL message".getBytes());
  2. 通过队列属性设置TTL

    创建队列时,可以通过队列的x-message-ttl参数来设置队列中消息的TTL。

    Map<String,Object> params = new HashMap<String, Object>();
    params.put("x-message-ttl",5000);
    channel.queueDeclare(QUEUE,false,false,false,params);

    上述代码将队列的消息ttl设置为5s。

对于第二种在队列上设置消息TTL到方式,消息一旦过期,会立刻被从队列中删除;而通过第一种发送消息时设置TTL的方式,消息过期后不一定会立即删除。这是由内部实现决定的,

对于第二种方式,队列中消息的TTL都相同,则消息过期顺序和入队顺序一致,那么只需要从队头定期删除消息即可;而第一种方式下,每条消息过期时间都不同,要实现"实时"删除

过期消息,得不断扫描整个队列,代价太大,所以等到消息即将被推送给消费者时在判断是否过期,如果过期就删除,是一种惰性处理策略。

示例

在以下示例中,创建来一个队列,并设置其中的消息TTL为20s,然后发送两条被路由到该队列的消息。第一条消息发送时设置了TTL为10s,这样,它到达队列后的TTL将为10s;

第二条消息发送时未设置TTL,它到达队列后的TTL为20s。

channel.exchangeDeclare(EXCHANGE,"fanout");

Map<String,Object> params = new HashMap<String, Object>();
params.put("x-message-ttl",20000);
channel.queueDeclare(QUEUE,false,false,false,params); channel.queueBind(QUEUE,EXCHANGE,""); AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
builder.expiration("10000"); //ttl 10s
AMQP.BasicProperties properties = builder.build();
channel.basicPublish(EXCHANGE,"",properties,"10s TTL message".getBytes()); channel.basicPublish(EXCHANGE,"",null,"20s TTL message".getBytes());

可以在RabbitMQ的Web管理页面或使用rabbitmqctl工具在命令行中看到,队列中到消息刚开时积攒了两条,10秒钟后第一条消息到达TTL未被消费,被从队列中丢弃,队列中

只剩第二条消息,在过10s,第二条消息也不丢弃。

队列的TTL

与消息TTL类型,可以为队列设置TTL。为队列中设置了TTL后,如果TTL时间内队列上没有消费者,或者队列没有被重新声明,那么队列将被服务端自动删除。

使用basic.QueueDeclare(channel.queueDeclare)声明队列时,通过x-expires参数可以设置队列的TTL。

声明一个ttl为10s的队列:

Map<String,Object> qArgs = new HashMap<String, Object>();
qArgs.put("x-expires",10000);
channel.queueDeclare(TEMP_QUEUE,false,false,false,qArgs);

死信

如果消息在队列中到达TTL,将被丢弃。这时候,消息变成死信(dead letter).过期是导致死信的原因之一,在RabbitMQ中,以下情况都会产生死信:

  • 消息过期
  • 消息被消费着拒绝(reject/nack),并且设置requeue参数为false
  • 队列到达最大长度

消息在队列中变成死信默认将被丢弃,为了处理死信,可以使用死信交换器(DLX)。

死信交换器可以认为是队列的备胎,当队列中产生死信时,死信被发送到死信交换器,由死信交换器重新路由到与之绑定的队列,这些队列被成为死信队列。

声明队列时,可以通过x-dead-letter-exchange参数设置该队列的死信交换器,也可以通过policy方式设定队列的死信交换器。

Map<String,Object> params = new HashMap<String, Object>();
params.put("x-dead-letter-exchange","dlx-exchange");
channel.queueDeclare("myqueue",false,false,false,params);

这样,当myquue队列中产生死信时,死信将被发送到dlx-exchange交换器,与它重新路由。

消息到路由键是后生产者发送是设置到,在死信被发送到死信交换器时,我们有机会修改消息到路由键。在声明队列是,指定x-dead-letter-routing-key参数即可。

params.put("x-dead-letter-routing-key","deadKey");

这样,当死信被发送到死信交换器时,它到路由键变为deadKey,后续在死信交换器中将根据该路由键进行路由。通过这种在队列上为死信统一更新路由键到方式,使得在某些

情况下可以统一将死信路由到指定队列,方便对死信统一处理。

RabbitMQ(4) 未路由的消息、TTL和死信的更多相关文章

  1. RabbitMQ 消费端限流、TTL、死信队列

    目录 消费端限流 1. 为什么要对消费端限流 2.限流的 api 讲解 3.如何对消费端进行限流 TTL 1.消息的 TTL 2.队列的 TTL 死信队列 实现死信队列步骤 总结 消费端限流 1. 为 ...

  2. RabbitMQ TTL、死信队列

    TTL概念 TTL是Time To Live的缩写,也就是生存时间. RabbitMQ支持消息的过期时间,在消息发送时可以进行指定. RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过 ...

  3. springboot整合rabbitmq实现生产者消息确认、死信交换器、未路由到队列的消息

    在上篇文章  springboot 整合 rabbitmq 中,我们实现了springboot 和rabbitmq的简单整合,这篇文章主要是对上篇文章功能的增强,主要完成如下功能. 需求: 生产者在启 ...

  4. RabbitMQ处理未被路由的消息

    我们经常使用消息队列进行系统之间的解耦,日志记录等等.但是有时候我们在使用 RabbitMQ时,由于exchange.bindKey.routingKey没有设置正确,导致我们发送给交换器(excha ...

  5. RabbitMQ消息可靠性、死信交换机、消息堆积问题

    目录 消息可靠性 生产者消息确认 示例 消费者消息确认 示例 死信交换机 例子 高可用问题 消息堆积问题 惰性队列 参考 消息可靠性 确保消息至少被消费了一次(不丢失) 消息丢失的几种情况: 消息在网 ...

  6. RabbitMQ系列二(构建消息队列)

    从AMQP协议可以看出,MessageQueue.Exchange和Binding构成了AMQP协议的核心.下面我们就围绕这三个主要组件,从应用使用的角度全面的介绍如何利用RabbitMQ构建消息队列 ...

  7. php如何使用rabbitmq实现发布消息和消费消息(一对多)(tp框架)(第二篇)

    一个publisher发布消息  多个个customer接受消息 1:准备工作参照: http://www.cnblogs.com/spicy/p/7886820.html 2,:路由: 3: 方法: ...

  8. RabbitMQ消费端ACK与重回队列机制,TTL,死信队列详解(十一)

    消费端的手工ACK和NACK 消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿. 如果由于服务器宕机等严重问题,那么我们就需要手工进行ACK保障消费端成功. 消费端重回队列 ...

  9. RabbitMQ常用的几种消息模型

    第一种模型(HelloWorld) 上图来自官方文档 P代表生产者用来生产消息,发送给消费者C,中间的共色部分代表消息队列,用来缓存消息. 首先导入依赖 <dependency> < ...

随机推荐

  1. iOS 开发,混合使用 ARC 和非ARC

    [前提知识] ARC:Automatic Reference Counting,自动引用计数 在开发 iOS 3 以及之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain. ...

  2. RabbitMQ的安装及集群搭建方法

    RabbitMQ安装 1 安装erlang 下载地址:http://www.erlang.org/downloads 博主这里采用的是otp_src_19.1.tar.gz (200MB+) [roo ...

  3. 20145310 《Java程序设计》第5周学习总结

    20145310 <Java程序设计>第5周学习总结 教材学习内容总结 本周主要进行第八章和第九章的学习. java中所有的错误都会打包为对象,可以try catch代表错误的对象后做一些 ...

  4. 20145328 《Java程序设计》实验四实验报告

    20145328 <Java程序设计>实验四实验报告 实验名称 Andoid开发基础 实验内容 基于Android Studio开发简单的Android应用并部署测试; 了解Android ...

  5. make menuconfig错误的解决办法

    如果使用make menuconfig的方式配置内核,又碰巧系统没有安装ncurses库(ubuntu系统默认就没有安装此库),就会出现错误,错误信息大体上如下: *** Unable to find ...

  6. Linux下的Nginx安装

    1 nginx安装环境 nginx是C语言开发,建议在linux上运行,本教程使用Centos6.5作为安装环境. gcc 安装nginx需要先将官网下载的源码进行编译,编译依赖gcc环境,如果没有g ...

  7. C++之STL迭代器(iterator)

    [摘要]本文是对STL--迭代器(iterator)的讲解,对学习C++编程技术有所帮助,与大家分享. 原文:http://www.cnblogs.com/qunews/p/3761405.html ...

  8. 解题报告:poj1083 Moving tables

    2017-09-02 19:49:59 writer:pprp 题意说明: 比较简单的题,一开始被吓到了,后来才发现,其实可以用很简单的方法就可以解决: 就是在这样的房间中如果在i 和 j 中之后的1 ...

  9. RabbitMQ 之 订阅模式 Publish/Subscribe

    模型图 我们之前学习的都是一个消息只能被一个消费者消费,那么如果我想发一个消息 能被多个消费者消费,这时候怎么办? 这时候我们就得用到了消息中的发布订阅模型 在前面的教程中,我们创建了一个工作队列,都 ...

  10. Linux安装jdk、删除Open jdk

    1.将jdk解压安装完成后,在bin目录下查看当前jdk的版本号 命令: ./java -version 2.编辑修改配置 1. 修改profile文件 进入命令:   vi  /etc/profil ...