热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

RabbitMQ基础概念进阶

上一篇RabbitMQ入门之基础概念介绍了RabbitMQ的一些基础概念,本文再来介绍其中的一些细节和其它的进阶的概念。一、消息生产者发送的消息不可达时如何处理RabbitMQ提供

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

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

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

1.1 mandatory 参数

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

RabbitMQ 基础概念进阶

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

RabbitMQ 基础概念进阶

通过查看 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 指定,示例代码如下:

RabbitMQ 基础概念进阶

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

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

RabbitMQ 基础概念进阶

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

1.2 队列 TTL 设置

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8S2FHXO6-1596969415042)(https://cdn.nlark.com/yuque/0/2020/png/285197/1596961384667-34a7e51a-5ecd-4d32-bfe2-e46ba173f91c.png?x-oss-process=image%2Fresize%2Cw_1500 “image.png”)]

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

三、死信队列介绍

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uwevqar7-1596969415044)(https://cdn.nlark.com/yuque/0/2020/png/285197/1596962590228-f7da6c9d-ef06-4284-8873-7cffa6351329.png?x-oss-process=image%2Fresize%2Cw_1500 “image.png”)]

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

RabbitMQ 基础概念进阶

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

四、延迟队列介绍

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVvvABGF-1596969415046)(https://cdn.nlark.com/yuque/0/2020/png/285197/1596964785505-925cb926-2429-4ccd-ac92-a6ebc49afc5e.png “image.png”)]

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

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

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

1.1 交换器持久化

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

1.2 消息持久化

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

RabbitMQ 基础概念进阶

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

1.3 队列持久化

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

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

五、总结

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


推荐阅读
  • 本文提供了 RabbitMQ 3.7 的快速上手指南,详细介绍了环境搭建、生产者和消费者的配置与使用。通过官方教程的指引,读者可以轻松完成初步测试和实践,快速掌握 RabbitMQ 的核心功能和基本操作。 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • IIS 7及7.5版本中应用程序池的最佳配置策略与实践
    在IIS 7及7.5版本中,优化应用程序池的配置是提升Web站点性能的关键步骤。具体操作包括:首先定位到目标Web站点的应用程序池,然后通过“应用程序池”菜单找到对应的池,右键选择“高级设置”。在一般优化方案中,建议调整以下几个关键参数:1. **基本设置**: - **队列长度**:默认值为1000,可根据实际需求调整队列长度,以提高处理请求的能力。此外,还可以进一步优化其他参数,如处理器使用限制、回收策略等,以确保应用程序池的高效运行。这些优化措施有助于提升系统的稳定性和响应速度。 ... [详细]
  • PHP中元素的计量单位是什么? ... [详细]
  • 深入解析 Django 中用户模型的自定义方法与技巧 ... [详细]
  • 掌握PHP框架开发与应用的核心知识点:构建高效PHP框架所需的技术与能力综述
    掌握PHP框架开发与应用的核心知识点对于构建高效PHP框架至关重要。本文综述了开发PHP框架所需的关键技术和能力,包括但不限于对PHP语言的深入理解、设计模式的应用、数据库操作、安全性措施以及性能优化等方面。对于初学者而言,熟悉主流框架如Laravel、Symfony等的实际应用场景,有助于更好地理解和掌握自定义框架开发的精髓。 ... [详细]
  • MySQL性能优化与调参指南【数据库管理】
    本文详细探讨了MySQL数据库的性能优化与参数调整技巧,旨在帮助数据库管理员和开发人员提升系统的运行效率。内容涵盖索引优化、查询优化、配置参数调整等方面,结合实际案例进行深入分析,提供实用的操作建议。此外,还介绍了常见的性能监控工具和方法,助力读者全面掌握MySQL性能优化的核心技能。 ... [详细]
  • 在软件开发领域,“池”技术被广泛应用,如数据库连接池、线程池等。本文重点探讨Java中的线程池ThreadPoolExecutor,通过详细解析其内部机制,帮助开发者理解如何高效利用线程池管理任务执行。线程池不仅能够显著减少系统资源的消耗,提高响应速度,还能通过合理的配置,如饱和策略,确保在高负载情况下系统的稳定性和可靠性。文章还将结合实际案例,展示线程池在不同应用场景下的具体实现与优化技巧。 ... [详细]
  • 题目旨在解决树上的路径最优化问题,具体为在给定的树中寻找一条长度介于L到R之间的路径,使该路径上的边权平均值最大化。通过点分治策略,可以有效地处理此类问题。若无长度限制,可采用01分数规划模型,将所有边权减去一个常数m,从而简化计算过程。此外,利用单调队列优化动态规划过程,进一步提高算法效率。 ... [详细]
  • 本研究基于状态空间方法,通过动态可视化技术实现了汉诺塔问题的求解过程,即将n个盘子从A柱移动到C柱。本文提供了一个使用C语言在控制台进行动画绘制的示例,并详细注释了程序逻辑,以帮助读者更好地理解和学习该算法。 ... [详细]
  • 在RabbitMQ中,消息发布者默认情况下不会接收到关于消息在Broker中状态的反馈,这可能导致消息丢失的问题。为了确保消息的可靠传输与投递,可以采用确认机制(如发布确认和事务模式)来验证消息是否成功抵达Broker,并采取相应的重试策略以提高系统的可靠性。此外,还可以配置消息持久化和镜像队列等高级功能,进一步增强消息的可靠性和高可用性。 ... [详细]
  • `ArrayDeque` 类中的 `removeLast()` 方法用于移除并返回双端队列中的最后一个元素。该方法在处理数据结构时非常有用,特别是在需要高效地从队列末尾移除元素的场景中。本文将详细介绍 `removeLast()` 方法的工作原理,并通过具体的应用实例展示其使用方法和优势。 ... [详细]
  • 本文探讨了Huffman树在数据结构中的应用及其原理。Huffman树,即哈夫曼树,是一种高效的数据压缩技术,通过构建最优二叉树实现编码,广泛应用于文件压缩和网络传输中,有效减少数据存储和传输的空间需求。 ... [详细]
  • LeetCode 算法挑战:最小栈的 Java 实现与优化 ... [详细]
  • 在 CentOS 7 上部署和配置 RabbitMQ 消息队列系统时,首先需要安装 Erlang,因为 RabbitMQ 是基于 Erlang 语言开发的。具体步骤包括:安装必要的依赖项,下载 Erlang 源码包(可能需要一些时间,请耐心等待),解压源码包,解决可能出现的错误,验证安装是否成功,并将 Erlang 添加到环境变量中。接下来,下载 RabbitMQ 的 tar.xz 压缩包,并进行解压和安装。确保每一步都按顺序执行,以保证系统的稳定性和可靠性。 ... [详细]
author-avatar
三砖先生
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有