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/begin01running/blog/916512

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值