RabbitMQ 基础概念进阶

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

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

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

1.1 mandatory 参数

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

image.png

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

image.png

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

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

1.2 immediate 参数

该的参数一样也是在channel.basicPublish 方法中定义的,其官方描述以下:3d

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.code

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

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

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

1.1 消息 TTL 设置

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

image.png

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

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

image.png

这种设置方式,即便队列过时也不会当即从队列中移除,由于每条消息是否过时的断定是在发送到消费者是才进行的,若是此时发现已通过期才会删除消息。而对于第一种方式则会把已通过期的消息移到队列头部,而后 RabbitMQ 只要按期的从头开始扫描是否存在过时的消息便可。

1.2 队列 TTL 设置

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

image.png

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

3、死信队列介绍

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

image.png

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

image.png

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

4、延迟队列介绍

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

image.png

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

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

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

1.1 交换器持久化

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

1.2 消息持久化

交换器的持久化仅仅只是保证了交换器自己的元数据不会丢失,没法保证其存储的消息不会丢失,若是须要其内部存储的消息不丢失,则须要设置消息的持久化,经过将消息的投递模式(deliveryMode)设置为 2 便可实现消息的持久化,以下所示:

image.png

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

1.3 队列持久化

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

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

5、总结

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

相关文章
相关标签/搜索