注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

网易杭研后台技术中心的博客

 
 
 
 
 

日志

 
 

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

来自郭忆   2013-03-25 23:06:06|  分类: 默认分类 |举报 |字号 订阅

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

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

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

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

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

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

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

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

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

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

  评论这张
 
阅读(9496)| 评论(1)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017