文章目录

  • 1、惰性队列
    • 1.1、使用场景
    • 1.2、定义
    • 1.3、队列模式
    • 1.4、工作流程
    • 1.5、总结
  • 2、镜像队列
    • 2.1、消息流转过程
    • 2.2、负载均衡
    • 2.3、消息的可靠性
    • 2.4、GM协议
    • 2.5、镜像队列宕机
    • 2.6、镜像队列启动与停止顺序

1、惰性队列

1.1、使用场景

从前几篇文章中,可以知道,整个消息中间件的运行过程如下图所示:

生产者将消息发送给rabbitMQ,然后消费者可以从对应的队列中读取消息进行消费,一般都是先从内存中读取消息,如果有持久化消息,那么再从磁盘中读取消息,当然如果从磁盘中找到了对应的消息就不需要再从磁盘中读取了。所以可以理解为消费者读取消息都是先内存读取再磁盘读取,消息一般存储在内存当中。

那么试想一下,当生产者生产消息足够快,或者说消费者机器宕机,那么内存中的消息是不是持续增加?那么就会内存压力非常大,当然你可以说我可以把一部分消息存储在磁盘当中啊,当然在消息量不大的时候,确实可行,但是是否考虑到在消息大爆发的时候,MQ服务器终究会撑不住,而且这样还会影响其它队列的消息收发。

那么是否有办法解决这一种情况呢?当然有的,rabbitMQ提供了一种惰性队列专门解决这种情况。现在来看一下官网给出的惰性队列的测试数据:

  • 对于内存的消耗测试:当发送1千万条消息,每条消息的大小为1KB,并且此时没有任何的消费者,那么普通队列会消耗1.2GB的内存,而惰性队列只消耗1.5MB的内存

  • 关于性能的测试:对于普通队列,如果要发送1千万条消息,需要耗费801秒,平均发送速度约为13000条/秒。如果使用惰性队列,那么发送同样多的消息时,耗时是421秒,平均发送速度约为24000条/秒。出现性能偏差的原因是普通队列会由于内存不足而不得不将消息换页至磁盘。如果有消费者消费时,惰性队列会耗费将近40MB的空间来发送消息,对于一个消费者的情况,平均的消费速度约为14000条/秒。

看完了上面的场景,现在来看一下啥是惰性队列吧。

1.2、定义

RabbitMQ从3.6.0版本开始引入了惰性队列(Lazy Queue)的概念。惰性队列会尽可能的将消息存入磁盘中,而在消费者消费到相应的消息时才会被加载到内存中,它的一个重要的设计目标是能够支持更长的队列,即支持更多的消息存储。当消费者由于各种各样的原因(比如消费者下线、宕机亦或者是由于维护而关闭等)而致使长时间内不能消费消息造成堆积时,惰性队列就很有必要了。

1.3、队列模式

队列具备两种模式:

  • default模式:默认的队列模式
  • lazy模式:惰性队列模式

在3.6.0之前的版本无需做任何变更。lazy模式即为惰性队列的模式,可以通过调用channel.queueDeclare方法的时候在参数中设置。也可以通过Policy的方式设置,如果一个队列同时使用这两种方式设置的话,那么Policy的方式具备更高的优先级。如果要通过声明的方式改变已有队列的模式的话,那么只能先删除队列,然后再重新声明一个新的惰性队列。

那么惰性队列能否与普通队列进行互换呢?当然可以了,如果要将普通队列转变为惰性队列,那么我们需要忍受同样的性能损耗。当转变为惰性队列的时候,首先需要将缓存中的消息换页至磁盘中,然后才能接收新的消息。反之,当将一个惰性队列转变为普通队列的时候,和恢复一个队列执行同样的操作,会将磁盘中的消息批量的导入到内存中。

1.4、工作流程
  • 普通队列

    • 即队列的模式是default模式的时候,当生产者将消息发送到RabbitMQ的时候,队列中的消息会尽可能的存储在内存之中,这样可以更加快速的将消息发送给消费者。
      对于持久化的消息,在被写入磁盘的同时也会在内存中驻留一份备份。当RabbitMQ需要释放内存的时候,会将内存中的消息换页至磁盘中,这个操作会耗费较长的时间,也会阻塞队列的操作,进而无法接收新的消息。
  • 惰性队列
    • 惰性队列会将接收到的消息直接存入文件系统中,而不管是持久化的或者是非持久化的,这样可以减少了内存的消耗,但是会增加I/O的使用,
    • 如果消息是持久化的,那么这样的I/O操作不可避免,惰性队列和持久化消息可谓是“最佳拍档”。
    • 注意如果惰性队列中存储的是非持久化的消息,内存的使用率会一直很稳定,但是重启之后消息一样会丢失
1.5、总结

综上所述,惰性队列就是用磁盘存储来替代内存存储,以此来减少内存的占用,避免由于内存不足而产生的换页操作。
但是对于消息量不是很大的情况,建议使用普通队列就可以了,因为内存读取速度会比磁盘读取速度快很多,因为磁盘读取会有很多的I/O操作。

2、镜像队列

RabbitMQ的镜像队列说到底就会为RabbitMQ的集群而生,它的使命就是为了提高集群的高可用性,关于RabbitMQ的集群可以查看上一篇文章<<一文了解Rabbit MQ的集群部署>>。 在上文中也对镜像队列做了介绍,镜像队列的结构如下图所示:

其中master是主节点(存放消息实体的队列),slave是从节点(镜像队列),一个主节点可以有多个从节点,消息实体 经过GM(Guaranteed Multicast)协议在主从镜像节点之间进行广播同步,这样每个从节点都保留和主节点一样的消息,所以在集群中,无论哪一台服务器节点宕机了,其它服务器节点照样可以工作。

那么当master节点崩溃后,会发生什么呢?什么都不会发生发生,因为还有slave节点啊,slave节点会保存消息体,
Rabbit MQ规定,当master节点宕机后,“资历最老"的 slave 会被提升为新的 master,根据 slave 加入的时间排序,时间最长的 slave 即为"资历最老”。

2.1、消息流转过程

当消费者与master队列建立连接,消费者可以直接从master队列上获取信息,当消费者与slave队列建立连接呢?消费者是从slave队列直接获取数据的吗?当然不是的,消息的流转顺序如下所示:

  • slave队列先将消费者的请求转发给master队列
  • 然后再由master队列准备好数据返回给slave队列
  • 最后由slave队列将消息返回给消费者

那这样就会有一个疑问?消费者的请求都是由master队列进行处理的,那么消息的负载是不是不能够做到有效的均衡呢?

2.2、负载均衡

Rabbit MQ的负载均衡是体现在物理机器层面上的,而不是体现在内存中的队列层面的。这样解释吧,现在有3台物理机,需要创建3个master队列和6个slave队列, 消息的请求负载都在3个master队列上,那么只需要将3个master队列和6个slave队列均匀的分布在3台物理机上,这样在很大程度上实现了每台机器的负载均衡。当然每个master队列消息请求的数量可能会有不同,无法保持绝对的负载均衡。

2.3、消息的可靠性

那么镜像队列是怎么保证消息传输的可靠性呢?

RabbitMQ的镜像队列使用 publisher confirm事务两种机制来保证其消息的可靠性。在事务机制中,只有当前事务在全部镜像中执行之后,客户端才会收到 Tx Commit-Ok 的消息。同样的,在 publisher confirm 机制中,生产者进行当前消息确认的前提是该消息被全部进行所接收了。

2.4、GM协议

GM协议能够保证组播消息的原子性,即保证组中活着的节点要么都收到消息要么都收不到,它的实现大致为:将所有的节点形成一个循环链表,每个节点都会监控位于自己左右两边的节点,当有节点新增时,相邻的节点保证当前广播的消息会复制到新的节点上 当有节点失效时,相邻的节点会接管以保证本次广播的消息会复制到所有的节点。

在 master 和slave上的这些GM 形成一个组 (gm_group) ,这个组的信息会记录在Mnesia 中。不同的镜像队列形成不同的组。操作命令从 master 对应的 GM 发出后,顺着链表传送到所有的节点。由于所有节点组成了一个循环链表,master 对应的 GM 最终会收到自己发送的操作命令,这个时候 master 就知道该操作命令都同步到了所有的slave上。

那么对于新节点的加入过程可以参考下图,整个过程就像在链表中间插入一个节点。注意每当一个节点加入或者重新加入到这个镜像链路中时,之前队列保存的内容会被全部清空。

2.5、镜像队列宕机

当slave 挂掉之后,除了与 slave 相连的客户端连接全部断开,没有其他影响。当 master挂掉之后,会有以下连锁反应

  • master 连接的客户端连接全部断开。
  • 选举最老的 slave 作为新的 master ,因为最老的 lave 与旧的 maste 之间的同步状态应
    该是最好的。如果此时所有 slave 处于未同步状态,则未同步的消息会丢失。
  • 新的 master 重新入队所有 unack 的消息,因为新的 lave 无法区分这些 unack 的消息是否己经到达客户端 ,或者是 ack 信息丢失在老的 master 链路上,再或者是丢失在老的master组播ack 消息到所有 slave 的链路上,所以出于消息可靠性的考虑,重新入队所有 unack 的消息,不过此时客户端可能会有重复消息。
  • 如果客户端连接着 slave ,并且 Basic.Consume 消费时指定了 x-cancel-o -hafailover 参数,那么断开之时客户端会收到一个 Consumer Cancellation Notification 的通知,消费者客户端中会回调 Consumer 接口的 handleCancel 方法。如果未指定x-cancelon-ha failover参数,那么消费者将无法感知master岩机。
2.6、镜像队列启动与停止顺序

镜像队列中最后一个停止的节点会是 master ,启动顺序必须是 master 先启动。如果 slave先启动,它会有 30 秒的等待时间,等待 master 的启动,然后加入到集群中。如果 30 秒内 master没有启动, slave 会自动停止。当所有节点因故(断电等)同时离线时,每个节点都认为自己不是最后一个停止的节点,要恢复镜像队列,可以尝试在 30 秒内启动所有节点。


欢迎各位关注我的JAVAERS公众号,陪你一起学习,一起成长,一起分享JAVA路上的诗和远方。在公众号里面都是JAVA这个世界的朋友,公众号每天会有技术类文章,面经干货,也有进阶架构的电子书籍,如Spring实战、SpringBoot实战、高性能MySQL、深入理解JVM、RabbitMQ实战、Redis设计与实现等等一些高质量书籍,关注公众号即可领取哦。


如果大家对人工智能感兴趣,可以关注下面公众号,会持续更新c++、python、tensorflow、机器学习、深度学习、计算机视觉等系列文章

RabbitMQ之惰性队列与镜像队列相关推荐

  1. RabbitMQ 的延时队列和镜像队列原理与实战

    在阿里云栖开发者沙龙PHP技术专场上,掌阅资深后端工程师.掘金小测<Redis深度历险>作者钱文品为大家介绍了RabbitMQ的延时队列和镜像队列的原理与实践,重点比较了RabbitMQ提 ...

  2. RabbitMQ集群搭建、镜像队列、实现高可用负载均衡、Federation Exchange、Federation Queue、Shovel

    1.环境准备 IP地址 主机名 192.168.56.20 conch01 192.168.56.21 conch02 192.168.56.22 conch03 2.安装 rabbitmq集群 1. ...

  3. 【深入理解RabbitMQ原理】RabbitMQ 普通队列与镜像队列 底层原理

    RabbitMQ 底层实现原理 普通MQ的结构 MQ内部大致又可以分为两部分:amqueue和backing queue, amqqueue负责实现amqp协议规定的mq的基本逻辑, backing ...

  4. RabbitMQ 高可用之镜像队列

    如果RabbitMQ集群只有一个broker节点,那么该节点的失效将导致整个服务临时性的不可用,并且可能会导致message的丢失(尤其是在非持久化message存储于非持久化queue中的时候).可 ...

  5. RabbitMQ镜像队列实现原理

    一.镜像队列使用 1.镜像队列作用 ​ RabbitMQ默认集群模式,并不包管队列的高可用性,尽管队列信息,交换机.绑定这些可以复制到集群里的任何一个节点,然则队列内容不会复制,固然该模式解决一项目组 ...

  6. Rabbitmq集群,镜像队列和分布式原理

    前言 基于前两次的分享会,结合rabbitmq相关知识,做一个小结.说明一致性的设计思想,在此说明相关的基础理论. CAP定理: 在计算机科学里,CAP定理又被称作布鲁尔定理(Brewer theor ...

  7. RabbitMQ之惰性队列(Lazy Queue)

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  8. RabbitMQ之镜像队列

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  9. rabbitmq——镜像队列

    转自:http://my.oschina.net/hncscwc/blog/186350?p=1 1. 镜像队列的设置 镜像队列的配置通过添加policy完成,policy添加的命令为: rabbit ...

最新文章

  1. Python--logging....实例应用
  2. java基础-可执行jar包
  3. python基础教程:常量 (最全常量解析)
  4. 跨平台APP JQuery Mobile开发-张晨光-专题视频课程
  5. Python数据分析之pandas入门
  6. springcloud hystrix概述(一)
  7. uestc oj 1218 Pick The Sticks (01背包变形)
  8. 平均薪资 38.4 万!3 步教你成为区块链开发者,收好这份学习指南!
  9. 整数、区间与区间端点
  10. 彻底下载32位office2010
  11. ACL在QinQ port 中的应用
  12. 4. HTTP Status Code
  13. 买的是ARM指令集授权,不是已设计好的CPU核?
  14. 数字媒体技术和数据科学与大数据技术_数据科学与大数据技术专业的女同学,迈出了她的舒适圈...
  15. 关于信号发生器的功能和参数介绍(二)
  16. flv怎么转换成mp4?
  17. chrome打开链接隐私设置错误_Chrome 隐私设置错误
  18. div高度设置100%无效的问题 (亲身实践)
  19. 高项.十大管理47个过程
  20. 一点一滴解读网狐的加解密

热门文章

  1. 数据库中的存储过程相关知识与语法
  2. Android 使用CountDownTimer实现定时任务
  3. 打好羽毛球—给学打羽毛球朋友的建议
  4. Random库中的8个函数
  5. [嵌入式er笔记]大端小端详解(含代码及详细注释)
  6. 一段js实现复制文本内容到剪切板(该方法兼容所有浏览器~)
  7. 计算机审计兴趣小组工作小结,关于科技兴趣小组活动总结.docx
  8. 安卓手机开不了机_苹果手机为什么会黑屏、开不了机?
  9. 一个好网站的策划设计
  10. 程序员的自我修炼---小白成长历程