RabbitMQ(4) 未路由的消息、TTL和死信
未路由的消息
当生产这发送的消息到达指定的交换器后,如果交换器无法根据自身类型、绑定的队列以及消息的路由键找到匹配的队列,默认情况下消息将被丢弃。可以通过两种方式
处理这种情况,一是在发送是设置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取两者中较小的。
设置单条消息都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());
通过队列属性设置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和死信的更多相关文章
- RabbitMQ 消费端限流、TTL、死信队列
目录 消费端限流 1. 为什么要对消费端限流 2.限流的 api 讲解 3.如何对消费端进行限流 TTL 1.消息的 TTL 2.队列的 TTL 死信队列 实现死信队列步骤 总结 消费端限流 1. 为 ...
- RabbitMQ TTL、死信队列
TTL概念 TTL是Time To Live的缩写,也就是生存时间. RabbitMQ支持消息的过期时间,在消息发送时可以进行指定. RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过 ...
- springboot整合rabbitmq实现生产者消息确认、死信交换器、未路由到队列的消息
在上篇文章 springboot 整合 rabbitmq 中,我们实现了springboot 和rabbitmq的简单整合,这篇文章主要是对上篇文章功能的增强,主要完成如下功能. 需求: 生产者在启 ...
- RabbitMQ处理未被路由的消息
我们经常使用消息队列进行系统之间的解耦,日志记录等等.但是有时候我们在使用 RabbitMQ时,由于exchange.bindKey.routingKey没有设置正确,导致我们发送给交换器(excha ...
- RabbitMQ消息可靠性、死信交换机、消息堆积问题
目录 消息可靠性 生产者消息确认 示例 消费者消息确认 示例 死信交换机 例子 高可用问题 消息堆积问题 惰性队列 参考 消息可靠性 确保消息至少被消费了一次(不丢失) 消息丢失的几种情况: 消息在网 ...
- RabbitMQ系列二(构建消息队列)
从AMQP协议可以看出,MessageQueue.Exchange和Binding构成了AMQP协议的核心.下面我们就围绕这三个主要组件,从应用使用的角度全面的介绍如何利用RabbitMQ构建消息队列 ...
- php如何使用rabbitmq实现发布消息和消费消息(一对多)(tp框架)(第二篇)
一个publisher发布消息 多个个customer接受消息 1:准备工作参照: http://www.cnblogs.com/spicy/p/7886820.html 2,:路由: 3: 方法: ...
- RabbitMQ消费端ACK与重回队列机制,TTL,死信队列详解(十一)
消费端的手工ACK和NACK 消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿. 如果由于服务器宕机等严重问题,那么我们就需要手工进行ACK保障消费端成功. 消费端重回队列 ...
- RabbitMQ常用的几种消息模型
第一种模型(HelloWorld) 上图来自官方文档 P代表生产者用来生产消息,发送给消费者C,中间的共色部分代表消息队列,用来缓存消息. 首先导入依赖 <dependency> < ...
随机推荐
- NGUI制作 《九宫格》 图片
什么是九宫格图片? 就是一张图片的上下左右四个角是固定的,无论X/Y被拉伸多大,图片都不会失真 效果图 ------> 那在NGUI里面怎么做到呢 1 首先你要把图片添加到NGUI图集里,点击E ...
- c++之旅:操作符重载
操作符重载 操作符重载可以为操作符添加更多的含义,操作符重载的作用的对象是类 那些操作符可以重载 除了下面几个操作符不能重载外,其它的操作符都能重载 . :: .* ?: sizeof 操作符重载的本 ...
- 新linux系统上rz 与sz命令不可用
使用命令进行安装:yum install lrzsz 即可使用
- jQuery与直接写JS的区别详细解析
jQuery代码具体的写法和原生的Javascript写法在执行常见操作时的区别如下所示.需要的朋友可以过来参考下 要使用jQuery,首先要在HTML代码最前面加上对jQuery库的引用,比 ...
- 20145214 《Java程序设计》第3周学习总结
教材学习内容总结 对象(Object):存在的具体实体,具有明确的状态和行为 类(Class):具有相同属性和行为的一组对象的集合,用于组合各个对象所共有操作和属性的一种机制 从类看对象:类定义可以视 ...
- 一文弄懂神经网络中的反向传播法——BackPropagation【转】
本文转载自:https://www.cnblogs.com/charlotte77/p/5629865.html 一文弄懂神经网络中的反向传播法——BackPropagation 最近在看深度学习 ...
- NOIP2015 T4 推销员 贪心+堆优化
前几天在学堆,这个数据结构貌似挺简单的,但是我看了很久啊QAQ... 今天算是搞懂了吧...于是想到了这道题...(当初悄悄咪咪看题解记得一点) 点我看题 放洛谷的题... 题意的话,大概就是有n个房 ...
- LeetCode——merge-two-sorted-lists
Question Merge two sorted linked lists and return it as a new list. The new list should be made by s ...
- MySQL级联删除和级联修改
1.新建主键table create table demo1_zhujian ( id int primary key auto_increment, name )); 2.新建外键table cre ...
- R中去除为NA的行--转载
下面用实例来说明这两个函数的作用: 这是一个数据框final: gene hsap mmul mmus rnor cfam 1 ENSG00000208234 0 NA NA NA NA 2 ENSG ...