RabbitMQ高可用--Quorum Queue(仲裁队列)的原理

原文网址:RabbitMQ高可用--Quorum Queue(仲裁队列)的原理_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文介绍RabbitMQ的Quorum Queue(仲裁队列)的用法和原理。

        RabbitMQ从3.8.0版本开始加入仲裁队列功能,它是镜像队列的替代方案。它有队列复制的能力,保障数据的高可用和安全性。使用仲裁队列可以在 RabbitMQ 节点间进行队列数据的复制,在一个节点宕机时队列仍然可以提供服务。

官网网址

https://www.rabbitmq.com/quorum-queues.html

相关网址

RabbitMQ高可用--Quorum Queue(仲裁队列)的用法_IT利刃出鞘的博客-CSDN博客

备注

        RabbitMQ 已经有一个高可用队列的实现:镜像队列(Mirror Queues)。在 RabbitMQ 3.8.0 版本之前,镜像队列是实现数据高可用的唯一手段,但它有设计缺陷。仲裁队列旨在解决镜像队列的性能和同步问题。

仲裁队列的特性

  1. 仲裁队列用 Raft 算法实现了持久的、复制的 FIFO 队列,更加专注于数据安全。
    1. 不会出现mirror queue出现的那样,两个节点件同一queue的数量落差很大的情况。
    2. Quorum队列中的消息要有集群中多半节点同意后,才会写到队列。这种队列类似于RocketMQ当中的Ledger集群,这种方式可以保证消息在集群内部不会丢失。
  2. Quorum queue到特定负载后,就不会继续接受新消息了。保持自身的平衡。

仲裁队列的使用场景

使用场景

  1. 队列长期存在
  2. 对容错、数据安全方面的要求比较高,能容忍响应慢些
  3. 不用持久化

不适合使用的场景

  1. 一些临时使用的队列:比如transient临时队列,exclusive独占队列,或者经常会修改和删除的队列
  2. 对消息低延迟要求高:一致性算法会影响消息的延迟
  3. 对数据安全性要求不高:Quorum队列需要消费者手动通知或者生产者手动确认
  4. 队列消息积压严重:如果队列中的消息很大,或者积压的消息很多,就不要使用Quorum队列。Quorum队列当前会将所有消息始终保存在内存中,直到达到内存使用极限。

仲裁队列和镜像队列的对比

Feature

Classic Mirrored

Quorum

Non-durable queues

yes

no

Exclusivity

yes

no

Per message persistence

per message

always

Membership changes

automatic

manual

Message TTL (Time-To-Live)

yes

yes (since 3.10)

Queue TTL

yes

yes

Queue length limits

yes

yes (except x-overflow: reject-publish-dlx)

Lazy behaviour

yes

always (since 3.10) or through the Memory Limit feature (before 3.10)

Message priority

yes

no

Consumer priority

yes

yes

Dead letter exchanges

yes

yes

Adheres to policies

yes

yes (see Policy support)

Poison message handling

no

yes

Global QoS Prefetch

yes

no

Poison Message(有毒的消息)

        这个是指消息一直不能被消费,就会导致消息不断的重新入队,这样这些消息就成为了毒消息。对于毒消息的处理,在经典队列中,可以通过设置入队次数上限的参数,将消息最终转入到死信队列处理,而在Quorum队列中,就有了更新的处理方式。

仲裁队列的优势

  1. 客户端不需要改变它们生产和订阅的方法,无需考虑队列类型。
    1. 唯一的区别就是在客户端定义队列的时候需要定义成仲裁队列(代码中添加相关属性)
  2. 被解决当节点重新上线时,不会丢数据(解决了镜像队列同步的问题)
    1. 主副本会直接从从副本中断的地方开始复制消息。复制的过程是非阻塞的,所以整个队列不会因为新的副本加入而收到影响。唯一的影响是网络使用率。
    2. 没有了同步问题,不仅让仲裁队列比镜像队列更可靠,同时,因为写入必须被超过半数的副本接受,所以不会因为脑裂而丢数据。
  3. Raft 协议比镜像队列的算法更有效率,可以提供更好的消息吞吐量。

总结起来,仲裁队列可以提供更高的性能、更好的数据安全性、更容易进行节点的滚动升级。

仲裁队列的劣势

特性更少

这些特性在仲裁队列的第一个版本中不会提供

  1. 非持久化消息
  2. 排它队列
  3. 队列/消息 TTL(超时时间)
  4. 一些规则(Policy)不可用,只有死信队列、队列长度限制可用
  5. 优先级
  6. 惰性队列
  7. 非全局的消息预取(Qos)

磁盘使用——写入放大

仲裁队列的磁盘和内存配置与普通队列不同。

普通队列

        普通队列使用“共享”存储模型,对于一条要投递到多个队列的消息,只会存储一次,其他队列只会保存这条消息的引用。也就是说,在发布-订阅模型下,一条将要投递到多个队列的消息,它的存储大小不会随着投递到的队列变多而线性增长。

        举个例子,我们用一个 fanout 类型的 exchange,绑定 10 个队列。这 10 个队列每个都设置 5个副本镜像队列。发布一条消息后,只有 5 条消息存储到集群中,每个节点存储 1 条。所以在这个情况下的写入放大是 5 倍。

仲裁队列

        仲裁队列在内存中使用“共享”的存储模型,在磁盘中,每条消息都会分别被存储。所以发布-订阅模型会造成更严重的写入放大,可能导致更大的磁盘使用,甚至不得不放弃使用仲裁队列。

        还是上面那个例子,当每个队列都变成仲裁队列,并且复制因子为 5 时,最终集群中的磁盘上存储了 50 条消息,每条消息的写入放大是 50 倍。

        因此,把 fanout 交换器和仲裁队列一起使用不太合适。

消息一直在内存中

        仲裁队列的所有消息一直会保存在内存中,这会增加内存的使用量,最终可能导致集群不可用。如果不进行一些检查和监控,队列消息不断堆积,可能会导致生产停止(内存高水位),直到消息被消费或者从内存中删除。所以当使用仲裁队列时,设置队列的长度限制非常重要。此外还有必要用惰性队列作为仲裁队列的死信队列,通过死信交换器将这些消息转发到死信队列中。

        因此,队列的规划和监控比普通场景更重要。下游(消费者和下游服务)的中断或者变慢可能导致多个队列消息堆积,需要有对应的规划和措施。你需要多少个仲裁队列、它们的写入速率时多少,当集群达到内存高水位时其他队列会不会收到影响?

失去多数节点时队列不可用

如果仲裁队列超过半数的副本永久丢失,那么队列数据就永久丢失了。即便有小部分的副本仍然可用,队列仍然没有办法恢复,只能被强制删除。虽然这种场景出现的可能性较小,但是仍有这样的危险存在。所以,推荐使用可靠的磁盘,并且把复制因子设置为 5 ~ 3。

延迟

        尽管仲裁队列的吞吐量更高,但是延迟也可能更高,这是由于使用了 Raft 协议。在仲裁队列中,所有消息都是持久化的,所有消息都会保存到每个副本的磁盘中。安全性是仲裁队列的主要目标。

猜你喜欢

转载自blog.csdn.net/feiying0canglang/article/details/126738084