未路由的消息

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

处理这种情况,一是在发送是设置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. 滑块控件CCControlSlider

    #include "cocos-ext.h" //包含头文件 using namespace cocos2d::extension;//引用命名空间 /** * 创建CCContr ...

  2. sparkSQL实战详解

    摘要   如果要想真正的掌握sparkSQL编程,首先要对sparkSQL的整体框架以及sparkSQL到底能帮助我们解决什么问题有一个整体的认识,然后就是对各个层级关系有一个清晰的认识后,才能真正的 ...

  3. Redis学习笔记之Redis单机,伪集群,Sentinel主从复制的安装和配置

    0x00 Redis简介 Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure server). Redis的键值 ...

  4. 深入学习js之——词法作用域和动态作用域

    开篇 当我们在开始学习任何一门语言的时候,都会接触到变量的概念,变量的出现其实是为了解决一个问题,为的是存储某些值,进而,存储某些值的目的是为了在之后对这个值进行访问或者修改,正是这种存储和访问变量的 ...

  5. servlet类与Spring Controller类的关系

    以前的java web项目,需要在web.xml中定义servlet,对应不同的请求,而在spring项目中,我们用controller定义了各种各样的servlet(当然不包括DispatcherS ...

  6. [HAOI2017模拟]囚人的旋律

    没有传送门辣. 神奇的DP题. 首先看到这道题第一眼应该想到正解不是在图上搞,肯定要把原图转化成序列. 根据逆序对的性质.每个点和标号大于他的点连边的点,其权值必定要小于该点,而没和他连边的且标号大于 ...

  7. linux目录结构及文件权限

    安装banner用到的指令: 第一步: sudo apt-get update 第二步: sudo apt-get install sysvbanner 成功了 创建新用户指令: sudo addus ...

  8. Java循环语句之 do...while

    do...while 循环与 while 循环语法有些类似,但执行过程差别比较大. 语法: 执行过程: <1>. 先执行一遍循环操作,然后判断循环条件是否成立 <2>. 如果条 ...

  9. Maven 三种archetype说明

    新建Maven project项目时,需要选择archetype. 那么,什么是archetype? archetype的意思就是模板原型的意思,原型是一个Maven项目模板工具包.一个原型被定义为从 ...

  10. java实现赋值excel模板,并在新文件中写入数据,并且下载

    /** * 生成excel并下载 */ public void exportExcel(){ File newFile = createNewFile(); //File newFile = new ...