RabbitMq基本架构

RabbitMq整体逻辑架构 


RabbitMQ Exchange类型

RabbitMQ常用的交换器类型有: fanout 、 direct 、 topic 、 headers 四种。

Fanout

会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中(广播方式

 Direct 

direct类型的交换器路由规则很简单,它会把消息路由到那些BindingKey和RoutingKey完全匹配的 队列中(指定了绑定队列/消费队列的传输方式)

 Topic(是对于Direct Exchange的拓展)

topic类型的交换器在direct匹配规则上进行了扩展,也是将消息路由到BindingKey和RoutingKey 相匹配的队列中,这里的匹配规则稍微不同。它约定:

BindingKey和RoutingKey一样都是由"."分隔的字符串;BindingKey中可以存在两种特殊字符“*”和 “#”,用于模糊匹配,其中"*"用于匹配一个单词,"#"用于匹配多个单词(可以是0个)

 Headers

headers类型的交换器不依赖于路由键的匹配规则来路由信息,而是根据发送的消息内容中的 headers属性进行匹配。在绑定队列和交换器时指定一组键值对,当发送的消息到交换器时, RabbitMQ会获取到该消息的headers,对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果匹配,消息就会路由到该队列。headers类型的交换器性能很差,不实用。


RabbitMq数据存储

消息类型: 持久化与非持久化消息,在RabbitMq中两种消息都会写入磁盘

持久化消息在到达队列时写入磁盘,同时会内存中保存一份备份,当内存吃紧时,消息从内存中清 除。这会提高一定的性能。

非持久化消息一般只存于内存中,当内存压力大时数据刷盘处理,以节省内存空间。

存储层结构:

队列索引:rabbit_queue_index,存放于queues文件夹下

索引维护队列的落盘消息的信息,如存储地点、是否已被给消费者接收、是否已被消费者ack等。 每个队列都有相对应的索引。

索引使用顺序的段文件来存储,后缀为.idx,文件名从0开始累加,每个段文件中包含固定的 segment_entry_count 条记录,默认值是16384。每个index从磁盘中读取消息的时候,至少要在内存 中维护一个段文件,所以设置 queue_index_embed_msgs_below 值得时候要格外谨慎,一点点增大也可能会引起内存爆炸式增长

 消息存储:rabbit_msg_store

消息以键值对的形式存储到文件中,一个虚拟主机上的所有队列使用同一块存储,每个节点只有一 个。存储分为持久化存储(msg_store_persistent)和短暂存储(msg_store_transient)。持久化存 储的内容在broker重启后不会丢失,短暂存储的内容在broker重启后丢失。

store使用文件来存储,后缀为.rdq,经过store处理的所有消息都会以追加的方式写入到该文件 中,当该文件的大小超过指定的限制(file_size_limit)后,将会关闭该文件并创建一个新的文件以供新 的消息写入。文件名从0开始进行累加。在进行消息的存储时,RabbitMQ会在ETS(Erlang Term Storage)表中记录消息在文件中的位置映射和文件的相关信息。

配置的使用:

文件rabbitmq.conf中queue_index_embed_msgs_below 来配置,默认值为4096B。当一个消息小于设定的大小阈值时,就 可以存储在index中,这样性能上可以得到优化。一个完整的消息大小小于这个值,就放到索引中,否 则放到持久化消息文件中。

## Size in bytes below which to embed messages in the queue index.
## Related doc guide: https://rabbitmq.com/persistence-conf.html
##
# queue_index_embed_msgs_below = 4096
## You can also set this size in memory units
##
# queue_index_embed_msgs_below = 4kb

消息的操作

删除操作

删除消息时,只是从ETS表删除指定消息的相关信息,同时更新消息对应的存储文件和相关信息。 在执行消息删除操作时,并不立即对文件中的消息进行删除,也就是说消息依然在文件中,仅仅是标记 为垃圾数据而已。当一个文件中都是垃圾数据时可以将这个文件删除。当检测到前后两个文件中的有效 数据可以合并成一个文件,并且所有的垃圾数据的大小和所有文件(至少有3个文件存在的情况下)的 数据大小的比值超过设置的阈值garbage_fraction(默认值0.5)时,才会触发垃圾回收,将这两个文件 合并,执行合并的两个文件一定是逻辑上相邻的两个文件

  • 1. 锁定这两个文件
  • 2. 先整理前面的文件的有效数据,再整理后面的文件的有效数据
  • 3. 将后面文件的有效数据写入到前面的文件中
  • 4. 更新消息在ETS表中的记录
  • 5. 删除后面文件


队列结构

RabbitMQ队列的4种状态:消息存入队列后,不是固定不变的,它会随着系统的负载在队列中不断流动,消息的状态会不断发 送变化

  • 1. alpha:消息索引和消息内容都存内存,最耗内存,很少消耗CPU
  • 2. beta:消息索引存内存,消息内容存磁盘
  • 3. gama:消息索引内存和磁盘都有,消息内容存磁盘
  • 4. delta:消息索引和内容都存磁盘,基本不消耗内存,消耗更多CPU和I/O操作

 对于普通没有设置优先级和镜像的队列来说,backing_queue的默认实现是 rabbit_variable_queue,其内部通过5个子队列Q1、Q2、delta、Q3、Q4来体现消息的各个状态。

当消费者获取消息时 :

  • 1. 首先会从Q4中获取消息,如果获取成功则返回。
  • 2. 如果Q4为空,则尝试从Q3中获取消息,系统首先会判断Q3是否为空,如果为空则返回队列为空,即此时队列中无消息。(到达Q3时,消息内容存磁盘,索引存内存,为空则不用判断delta都存磁盘的情况)
  • 3. 如果Q3不为空,则取出Q3中的消息;进而再判断此时Q3和Delta中的长度,如果都为空,则 可以认为 Q2、Delta、 Q3、Q4 全部为空,此时将Q1中的消息直接转移至Q4,下次直接从 Q4 中获取消息。
  • 4. 如果Q3为空,Delta不为空,则将Delta的消息转移至Q3中,下次可以直接从Q3中获取消息。 在将消息从Delta转移到Q3的过程中,是按照索引分段读取的,首先读取某一段,然后判断读 取的消息的个数与Delta中消息的个数是否相等,如果相等,则可以判定此时Delta中己无消 息,则直接将Q2和刚读取到的消息一并放入到Q3中,如果不相等,仅将此次读取到的消息转移到Q3。

面试问题:为什么消息的堆积导致性能下降? 

如果Q4为空,那么可以直接走Q1,全部在内存中操作给消费端,消息积压之后,内存快满了,然后就会走Q3,这时候索引存内存,消息内容存磁盘,会导致一定的IO,性能有所下降。然后内存满了,那么就会走delta,索引与消息内容全部存磁盘,虽然保证了可用性,但是会产生大量的IO,直接影响其性能。

猜你喜欢

转载自blog.csdn.net/qq_42773863/article/details/121479270