上一篇 RabbitMQ 入门之基础概念 介绍了 RabbitMQ 的一些基础概念,本文再来介绍其中的一些细节和其它的进阶的概念。

一、消息生产者发送的消息不可达时如何处理

RabbitMQ 提供了消息在传递过程中无法发送到一个队列(比如根据自己的类型和路由键没有找到匹配的队列)时将消息回传给消息发送方的功能,使用 RabbitMQ 的客户端提供 channel.basicPublish 方法的两个参数 mandatory 和 immediate (RabbitMQ 3.0 以下版本),除此之外还提供了一个备份交换器可以将无法发送的消息存储起来处理,不用重新传回给发送方。

1.1 mandatory 参数

mandatory 被定义在 RabbitMQ 提供的客户端的 channel.basicPublish 方法中,如下所示:

当把方法的 mandatory 参数设置为 true 时,那么会在交换器无法根据自身的类型和路由键找到一个符合要求的队列时,RabbitMQ 会自动调用 Basic.Return 把该消息回传给发送方也就是我们的消息生产者。反之,如果设置为 false 的话,消息就会被直接丢弃掉。那么问题来了,我们要如何去获取这些没有被发送出去的消息呢?RabbitMQ 给我们提供了事件监听机制来获取这种消息,可以通过 addReturnListener 方法添加一个 ReturnListener 来获取这种未发送到队列的消息,如下所示:

通过查看 ReturnListener 接口的源码可以看到,该接口只有一个方法,如果是 JDK8+ 的版本的话可以使用 Lambda 表达式来简化一些代码。

可以看出,当设置了 mandatory 参数时,还必须为生产者同时添加 ReturnListener 监听器的编程逻辑,这样就会使得生产者的代码变得更加复杂了,为了处理这种情况,RabbitMQ 提供了 `备份交换器` 来将没有成功路由出去的消息存储起来,当我们需要的时候再去处理即可。

1.2 immediate 参数

该的参数同样也是在channel.basicPublish 方法中定义的,其官方描述如下:

This flag tells the server how to react if the message cannot be routed to a queue consumer immediately. If this flag is set, the server will return an undeliverable message with a Return method. If this flag is zero, the server will queue the message, but with no guarantee that it will ever be consumed.

当把 immediate 参数设置为 true 时,如果交换器根据其类型和路由键找到符合要求的队列时,发现所有队列上没有任何消费者,则该消息并不会存入到队列中,会通过 Basic.Return 命令把消息回传给生产者。简而言之也就是说,当设置了 immediate 参数时,该消息关联的队列上存在消费者时,会立即发送消息到该队列中,反之如果匹配的队列上不存在任何消费者,则直接把消息回传给生产者。这里有一点需要注意的是:从 RabbitMQ 3.0 + 已经去除了该参数。

二、如何对消息和队列设置过期时间 (TTL)

TTL 是 time to live 首字母的简称,RabbitMQ 中可以设置消息和队列的过期时间,我们先来看看要如何设置消息的过期时间。

1.1 消息 TTL 设置

RabbitMQ 提供了两种设置消息的过期时间,第一种是通过队列的属性设置,该方式的特点就是队列中所有消息的过期时间都一致。还有一种是更小粒度的设置,就是对每条消息单独设置过期时间,这种方式更加灵活,每条消息的过期时间都可以不一样。这是你可能会问,如果同时设置了队列的过期属性和消息本身的过期属性,最终以哪个为准呢?结果是 RabbitMQ 会比较这两个 TTL 的值大小,以较小的那个为准。很容易想到,通过队列的属性的方式设置过期时间的话是在声明队列的时候指定,对应到客户端就是其提供的 channel.queueDeclare 方法的参数 arguments 指定,示例代码如下:

需要注意的是 x-message-ttl 参数的单位是毫秒。如果不设置 TLL,则表示该消息不会过期,如果将 TTL 设置为 0,表示除非此时可以把消息直接发送投递到消费者端去,否则就会直接丢弃该消息。

准对每条消息设置 TTL 的方法是在发送消息的时候设置的,对应到客户端方法是 channel.basicPublish 的 expiration 属性参数,具体设置代码如下:

这种设置方式,即使队列过期也不会立即从队列中移除,因为每条消息是否过期的判定是在发送到消费者是才进行的,如果此时发现已经过期才会删除消息。而对于第一种方式则会把已经过期的消息移到队列头部,然后 RabbitMQ 只要定期的从头开始扫描是否存在过期的消息即可。

1.2 队列 TTL 设置

设置队列的过期时间使用的是客户端的 channel.queueDeclare 方法参数中的 x-expires 参数,其单位同样也是毫秒,不过需要注意的是它不能设置为 0。设置队列过期的代码如下所示:

上面代码创建了一个过期时间为 15 分钟的队列。

三、死信队列介绍

死信交换器(DLX)的全称是 Dead-Letter-Exchange ,也称之为死信邮箱。简单来说就是当一个消息由于 消息被拒绝 、 消息过期 、 队列达到最大长度 时,变成死信(dead message)之后,会被重新发送到一个交换器中,这个交换器就是死信交换器,绑定在这个交换器上的队列就称之为死信队列。死信交换器实际上就是平常的交换器,可以在任何队列上指定,当在一个队列上设置死信交换器后,如果该队列出现死信时就会被 RabbitMQ 把死信消息重新发送到死信交换器上去,然后路由到死信队列中,我们可以监听这个队列来处理那些死信消息。为一个队列设置死信交换器是在生产者的声明队列的方法中设置 x-dead-letter-message 参数来实现的,如下所示:

同时也可以通过 x-dead-letter-routing-key 参数设置死信交互器的路由键,不设置默认使用原始度列的路由键。可以到 RabbitMQ 的后台管理界面,有 DLX 标志的就是死信队列。

RabbitMQ 提供的 DLX 是个比较实用的功能特性,它可以在我们消息不能被消费者正确消费的情况下放入到死信队列,后续我们可以通过这个死信队列的内容来查看异常情况来改造和优化系统。

四、延迟队列介绍

顾名思义,延迟队列存储的是哪些需要等待指定时间后才能拿到的延迟消息,一个比较典型的场景就是订单 30 分钟后未支付取消订单。这里需要注意的是,在 RabbitMQ 中并没有直接提供延迟队列的功能,而是需要通过上面介绍的过期时间(TTL)和死信队列一起来实现,比如超时取消订单这个场景,我们可以让消费者订阅死信队列,设置正常的那个队列的超时时间为 30 分钟并绑定到该死信队列上,当消息超过 30 分钟未被处理后消息就会把发送到死信队列中,然后死信队列的消费者就可以在 30 分钟后成功的消费到该消息了。

同时当我们有其它的超时配置需求时也很方便扩展,比如可以在生产者发送消息的时候通过设置不同的路由键,通过路由键来将消息发送到与交换器绑定的不同队列中,然后这些队列分别设置不同的过期时间和与之相对应的死信队列,当消息过期时就会被 RabbitMQ 转发到相应的死信队列中,这样就可以去订阅相应的死信队列即可。

五、交换器、消息和队列持久化

持久化可以提高可靠性,可以防止宕机或者重启等异常下数据的丢失,RabbitMQ 的持久化从组成结构上可以分为三个部分,即交换器持久化、消息持久化和队列持久化。

1.1 交换器持久化

交换器持久化是在声明交换器时将 durable 参数设置为 true 来实现的。如果不设置持久化属性的话,当 RabbitMQ 服务重启后交换器的数据就会丢失,需要注意的是,是交换器的数据丢失,消息不会丢失,只是不能将消息发送到这个交换器中了,一般生产环境使用都会把该属性设置为持久化。

1.2 消息持久化

交换器的持久化仅仅只是保证了交换器本身的元数据不会丢失,无法保证其存储的消息不会丢失,如果需要其内部存储的消息不丢失,则需要设置消息的持久化,通过将消息的投递模式(deliveryMode)设置为 2 即可实现消息的持久化,如下所示:

需要消息持久化的前提是其所在的队列也要设置持久化,假如仅仅只设置消息的持久化的话,RabbitMQ 重启之后队列消失,然后消息也会丢失。这里有点需要注意一下,虽然持久化可以提高可靠性,但是持久化是将数据存储到硬盘上,比直接操作内存要慢很多,所以对于哪些可靠性要求不高的业务不需要进行持久化。

1.3 队列持久化

队列的持久化的设置和交换器持久化类似,同样也是在声明的时候通过 durable 参数设置为 true 实现的,如果不设置,当 RabbitMQ 重启后,相关的队列元数据也会丢失,相应的其存储的消息也会随之丢失掉。

将交换器、队列、消息都设置了持久化之后就能百分之百保证数据不丢失了吗?其实无法保证百分之百数据不丢失。比如消费者在订阅消费队列时将自动应答(autoAck)参数设置为 true 的话,在接收到消息后还没来得及处理就挂了,这时需要把自动应答设置 false,进行手动 ack 应答即可。还有一个就是由于不是实时持久化存盘,当消息存盘的过程中 RabbitMQ 宕机了,此时也会发生数据丢失,此时需要通过 RabbitMQ 的 镜像队列机制 来处理了。

五、总结

本文主要介绍了一些参数具体使用时的设置细节和死信队列、延迟队列以及持久化等,还有一些比较重要的点没有涉及到,比如消息确认机制。“纸上得来终觉浅,绝知此事要躬行”,在了解一些基础的概念之后还是需要通过具体编码实践才能对其更加理解深刻。

RabbitMQ 基础概念进阶的更多相关文章

  1. RabbitMQ基础概念详细介绍

    http://blog.csdn.net/column/details/rabbitmq.html 转至:http://www.ostest.cn/archives/497 引言 你是否遇到过两个(多 ...

  2. RabbitMQ基础概念及使用

    RabbitMQ RabbitMQ是什么? RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议.MQ全称为Message Q ...

  3. RabbitMQ基础概念

    转至:http://blog.csdn.net/whycold/article/details/41119807 RabbitMQ简介 AMQP,即Advanced Message Queuing P ...

  4. RabbitMQ 基础概念介绍

    AMQP 消息模型 RabbitMQ 是基于 AMQP(高级消息队列协议)的一个开源实现,其内部实际也是 AMQP 的基本概念.

  5. RabbitMQ 基础概念

    Broker:消息协商器.消息队列的实体,它在TCP/IP等端口上监听AMQ消息 vHost:虚拟主机.功能上类似于web的虚拟主机,都是把数据按照功能或项目的不同划分为不同的虚拟主机:用户只被授予访 ...

  6. RabbitMQ基础概念(消息、队列、交换机)

    1.消息的确认 RabbitMQ需要对每一条发送的消息进行确认.消费者必须通过AMQP的basic.ack命令显式地向RabbitMQ发送一个确认,或者在订阅到队列的时候就将auto_ack参数设置为 ...

  7. RabbitMQ基本概念和原理

    RabbitMQ基本概念和原理 1.AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计. 2.Rabb ...

  8. RabbitMq基础教程之基本概念

    RabbitMq基础教程之基本概念 RabbitMQ是一个消息队列,和Kafka以及阿里的ActiveMQ从属性来讲,干的都是一回事.消息队列的主要目的实现消息的生产者和消费者之间的解耦,支持多应用之 ...

  9. RabbitMQ基础教程之使用进阶篇

    RabbitMQ基础教程之使用进阶篇 相关博文,推荐查看: RabbitMq基础教程之安装与测试 RabbitMq基础教程之基本概念 RabbitMQ基础教程之基本使用篇 I. 背景 前一篇基本使用篇 ...

随机推荐

  1. 掌握Redis分布式锁的正确姿势

    本文中案例都会在上传到git上,请放心浏览 git地址:https://github.com/muxiaonong/Spring-Cloud/tree/master/order-lock 本文会使用到 ...

  2. p40_数据交换方式

    一.为什么要数据交换 数据链路发展史: 二.数据交换方式 电路交换 报文交换 分组交换[数据报方式,虚电路方式] 三.电路交换 eg:电话网络(特点:**独占资源,**即使两个人不说话,链接也不会被别 ...

  3. 高效C++:继承和实现

    如何正确的使用继承和实现是本章说明的重点. 确定public继承的关系是is-a public继承等同于is-a 对public继承,所有base的特性,在derived上都适用 避免遮掩继承而来的名 ...

  4. vue中引入jq的步骤--以及注意事项

  5. 轻量级分布式延时任务处理组件easyTask-L-入门篇

    今天给大家介绍一款新武器.我自研的一个java组件easyTask-L.这个是做啥的呢?我之前研发了一款单机版本的easyTask,这次是要介绍另外一款easyTask-L.区别就是后者支持分布式环境 ...

  6. 关于ajaxSubmit传递参数 后台接收为"参数,参数”的问题

    问题: 用户名密码往后台提交的时候,发现接收到的参数变成了下图 解决办法: 去掉ajaxSubmit的data属性 如下图 解释:因为ajaxSubmit在封装的时候会自动的从被form包裹的表单控件 ...

  7. springboot(八)内置SpringMvc静态文件地址修改

    参考:作者:恒宇少年链接:https://www.jianshu.com/p/c6ab1081fd5f   介绍: SpringMVC大家都不陌生,而被SpringBoot集成的SpringMVC除了 ...

  8. ElasticSearch(一)概念介绍及环境搭建

    一.什么是ElasticSearch: Elasticsearch (ES)是一个基于Lucene构建的开源.分布式.RESTful 接口全文搜索引擎.Elasticsearch 还是一个分布式文档数 ...

  9. 【NeurlPS2019】Positional Normalization 位置归一化

    作者提出,当前的BatchNorm, GroupNorm, InstanceNorm在空间层面归一化信息,同时丢弃了统计值.作者认为这些统计信息中包含重要的信息,如果有效利用,可以提高GAN和分类网络 ...

  10. Centos 7下编译安装Mysql

    (1)官网下载地址:https://dev.mysql.com/downloads/mysql/ 此处下载的是 mysql-boost-5.7..tar.gz 百度云下载地址:https://pan. ...