RabbitMQ系列三 (深入消息队列)

消息持久化是 RabbitMQ 最为人津津乐道的特性之一, RabbitMQ 能够在付出最小的性能代价的基础上实现消息的持久化,最大的奥秘就在于 RabbitMQ 多层消息队列的设计上。下面,本文就从 MessageQueue 的设计和消息在 MessageQueue 的生命周期两个方面全面介绍  RabbitMQ 的消息队列。

   RabbitMQ完全实现了AMQP协议,类似于一个邮箱服务。Exchange负责根据ExchangeType和RoutingKey将消息投递到对应的消息队列中,消息队列负责在消费者获取消息前暂存消息。在RabbitMQ中,MessageQueue主要由两部分组成,一个为AMQQueue,主要负责实现AMQP协议的逻辑功能。另外一个是用来存储消息的BackingQueue,本文重点关注的是BackingQueue的设计。

   RabbitMQ系列三 (深入消息队列) - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客

    RabbitMQ中BackingQueue又由5个子队列组成:Q1、Q2、Delta、Q3和Q4。RabbitMQ中的消息一旦进入队列,不是固定不变的,它会随着系统的负载在队列中不断流动,消息的状态不断发生变化。RabbitMQ中的消息一共有5种状态:

   a)Alpha:消息的内容和消息索引都保存在内存中;

   b)Beta:消息内容保存在磁盘上,消息索引保存在内存中;

   c)Gamma:消息内容保存在磁盘上,消息索引在磁盘和内存都有;

   d)Delta:消息内容和索引都在磁盘上;

   注意:对于持久化的消息,消息内容和消息索引都必须先保存到磁盘上,才会处于上述状态中的一种,而Gamma状态的消息只有持久化的消息才会有该状态。

      BackingQueue 中的 5 个子队列中的消息状态, Q1 和 Q4 对应的是 Alpha 状态, Q2 和 Q3 是 Beta 状态, Delta 对应的是 Delta 状态。上述就是 RabbitMQ 的多层队列结构的设计,我们可以看出从 Q1 到 Q4 ,基本经历的是由 RAM 到 DISK ,再到 RAM 的设计。这样的设计的好处就是当队列负载很高的情况下,能够通过将一部分消息由磁盘保存来节省内存空间,当负载降低的时候,这部分消息又渐渐回到内存,被消费者获取,使得整个队列有很好的弹性。下面我们就来看一下,整个消息队列的工作流程。
     引起消息流动主要有两方面的因素:其一是消费者获取消息;其二是由于内存不足,引起消息的换出到磁盘上( Q1-.>Q2 、 Q2->Delta 、 Q3->Delta 、 Q4->Q3 )。 RabbitMQ 在系统运行时会根据消息传输的速度计算一个当前内存中能够保存的最大消息数量( Target_RAM_Count ),当内存中的消息数量大于该值时,就会引起消息的流动。进入队列的消息,一般会按着 Q1->Q2->Delta->Q3->Q4 的顺序进行流动,但是并不是每条消息都一定会经历所有的状态,这个取决于当时系统的负载状况。
       当消费者获取消息时,首先会从 Q4 队列中获取消息,如果 Q4 获取成功,则返回,如果 Q4 为空,则尝试从 Q3 获取消息;首先,系统会判断 Q3 队列是否为空,如果为空,则直接返回队列为空,即此时队列中无消息(后续会论证)。如果不为空,则取出 Q3 的消息,然后判断此时 Q3 和 Delta 队列的长度,如果都为空,则可认为 Q2 、 Delta 、 Q3 和 Q4 全部为空 ( 后续说明 ) ,此时将 Q1 中消息直接转移到 Q4 中,下次直接从 Q4 中获取消息。如果 Q3 为空, Delta 不空,则将 Delta 中的消息转移到 Q3 中;如果 Q3 非空,则直接下次从 Q3 中获取消息。在将 Delta 转移到 Q3 的过程中, RabbitMQ 是按照索引分段读取的,首先读取某一段,直到读到的消息非空为止,然后判断读取的消息个数与 Delta 中的消息个数是否相等,如果相等,则断定此时 Delta 中已无消息,则直接将 Q2 和刚读到的消息一并放入 Q3 中。如果不相等,则仅将此次读到的消息转移到 Q3 中。这就是消费者引起的消息流动过程。
    RabbitMQ系列三 (深入消息队列) - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客
     下面我们分析一下由于内存不足引起的消息换出。消息换出的条件是内存中保存的消息数量 + 等待 ACK 的消息的数量 >Target_RAM_Count 。当条件触发时,系统首先会判断如果当前进入等待 ACK 的消息的速度大于进入队列的消息的速度时,会先处理等待 ACK 的消息。步骤基本上 Q1->Q2 或者 Q3 移动,取决于 Delta 队列是否为空。 Q4->Q3 移动, Q2 和 Q3 向 Delta 移动。
    最后,我们来分析一下前面遗留的两个问题,一个是为什么 Q3 队列为空即可认定整个队列为空。试想如果 Q3 为空, Delta 不空,则在 Q3 取出最后一条消息时, Delta 上的消息就会被转移到 Q3 上,与 Q3 空矛盾。如果 Q2 不空,则在 Q3 取出最后一条消息,如果 Delta 为空时,会将 Q2 的消息并入 Q3 ,与 Q3 为空矛盾。如果 Q1 不空,则在 Q3 取出最后一条消息,如果 Delta 和 Q3 均为空时,则将 Q1 的消息转移到 Q4 中,与 Q4 为空矛盾。这也解释了另外一个问题,即为什么 Q3 和 Delta 为空, Q2 就为空。
    上述就是整个消息在 RabbitMQ 队列中流动过程。从上述流程可以看出,消息如果能够被尽早消费掉,就不需要经历持久化的过程,因为这样会加系统的开销。如果消息被消费的速度过慢, RabbitMQ 通过换出内存的方式,防止内存溢出。

转载于:https://my.oschina.net/u/551903/blog/487112

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值