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

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

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

在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
中。这就是消费者引起的消息流动过程。

下面我们分析一下由于内存不足引起的消息换出。消息换出的条件是内存中保存的消息数量
+
等待
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
通过换出内存的方式,防止内存溢出。

时间: 2024-08-28 18:24:30

RabbitMQ系列三 (深入消息队列)的相关文章

大型网站架构系列:分布式消息队列(一) (转)

以下是消息队列以下的大纲,本文主要介绍消息队列概述,消息队列应用场景和消息中间件示例(电商,日志系统). 本次分享大纲 消息队列概述 消息队列应用场景 消息中间件示例 JMS消息服务(见第二篇:大型网站架构系列:分布式消息队列(二)) 常用消息队列(见第二篇:大型网站架构系列:分布式消息队列(二)) 参考(推荐)资料(见第二篇:大型网站架构系列:分布式消息队列(二)) 本次分享总结(见第二篇:大型网站架构系列:分布式消息队列(二)) 一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要

大型网站架构系列:分布式消息队列(二)

本文是大型网站架构系列:消息队列(二),主要分享JMS消息服务,常用消息中间件(Active MQ,Rabbit MQ,Zero MQ,Kafka).[第二篇的内容大部分为网络资源的整理和汇总,供大家学习总结使用,最后有文章来源] 本次分享大纲 消息队列概述(见第一篇:大型网站架构系列:分布式消息队列(一)) 消息队列应用场景(见第一篇:大型网站架构系列:分布式消息队列(一)) 消息中间件示例(见第一篇:大型网站架构系列:分布式消息队列(一)) JMS消息服务 常用消息队列 参考(推荐)资料 本

大型网站架构系列:分布式消息队列(一)(转)

大型网站架构系列:分布式消息队列(一) 以下是消息队列以下的大纲,本文主要介绍消息队列概述,消息队列应用场景和消息中间件示例(电商,日志系统). 本次分享大纲 消息队列概述 消息队列应用场景 消息中间件示例 JMS消息服务(见第二篇:大型网站架构系列:分布式消息队列(二)) 常用消息队列(见第二篇:大型网站架构系列:分布式消息队列(二)) 参考(推荐)资料(见第二篇:大型网站架构系列:分布式消息队列(二)) 本次分享总结(见第二篇:大型网站架构系列:分布式消息队列(二)) 一.消息队列概述 消息

【系统架构】读《大型网站架构系列:分布式消息队列》整理

文章地址 拓展阅读: RabbitMQ + PHP (一)入门与安装 RabbitMQ + PHP (二)AMQP拓展安装 RabbitMQ + PHP (三)案例演示 1. 一些词汇和技术 1)Zookeeper注册中心 Storm集群 2. 核心思想 1)[概述] 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使用较多的消息队列有ActiveMQ,Rabbit

大型网站架构系列:分布式消息队列(一)

以下是消息队列以下的大纲,本文主要介绍消息队列概述,消息队列应用场景和消息中间件示例(电商,日志系统). 本次分享大纲 消息队列概述 消息队列应用场景 消息中间件示例 JMS消息服务 常用消息队列 参考(推荐)资料 本次分享总结 一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,K

Kafka 消息队列系列之分布式消息队列Kafka

介绍 ApacheKafka®是一个分布式流媒体平台.这到底是什么意思呢?我们认为流媒体平台具有三个关键功能:它可以让你发布和订阅记录流.在这方面,它类似于消??息队列或企业消息传递系统.它允许您以容错方式存储记录流.它可以让您在发生记录时处理记录流.什么是卡夫卡好?它被用于两大类的应用程序:构建可在系统或应用程序之间可靠获取数据的实时流数据管道构建实时流应用程序,可以转换或响应数据流要了解卡夫卡如何做这些事情,让我们深入探索卡夫卡的能力.首先几个概念:Kafka作为一个或多个服务器上的集群运行

rabbitmq系列三 之发布/订阅

1.发布/订阅 在上篇教程中,我们搭建了一个工作队列,每个任务只分发给一个工作者(worker).在本篇教程中,我们要做的跟之前完全不一样 -- 分发一个消息给多个消费者(consumers).这种模式被称为"发布/订阅". 为了描述这种模式,我们将会构建一个简单的日志系统.它包括两个程序--第一个程序负责发送日志消息,第二个程序负责获取消息并输出内容. 在我们的这个日志系统中,所有正在运行的接收方程序都会接受消息.我们用其中一个接收者(receiver)把日志写入硬盘中,另外一个接受

rabbitmq和redis用作消息队列的区别

将redis发布订阅模式用做消息队列和rabbitmq的区别: 可靠性   redis :没有相应的机制保证消息的可靠消费,如果发布者发布一条消息,而没有对应的订阅者的话,这条消息将丢失,不会存在内存中:rabbitmq:具有消息消费确认机制,如果发布一条消息,还没有消费者消费该队列,那么这条消息将一直存放在队列中,直到有消费者消费了该条消息,以此可以保证消息的可靠消费: 实时性 redis:实时性高,redis作为高效的缓存服务器,所有数据都存在在服务器中,所以它具有更高的实时性 消费者负载均

8.windows消息机制(三)消息队列

1.消息队列 消息队列用于存放消息的一个队列,消息在队列中先入先出.所有窗口程序都具有消息队列,程序可以从队列中获取消息. 2.消息队列的类型 系统消息队列 - 由系统维护的消息队列,存放系统产生的消息,例如鼠标.键盘等. 程序消息队列 - 属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护. 3.消息队列的关系 当鼠标.键盘产生消息时,会将消息存放到系统消息队列. 系统会根据存放的消息,找到对应窗口的消息队列.