消息队列是我们在日常业务开发中频繁使用的中间件,我们对它的使用方式、功能特性和基本原理都有一定的了解。但是消息队列服务端身为一个分布式系统,它也就一定满足CAP理论,我们也不得不考虑它运行时的可用性与可靠性,接下来就介绍一下各大主流消息队列中间件在可用性这方面的取舍。

高可用性

在分布式系统中,高可用性(High Availability)指的是在一个高可用的分布式系统中,当发生硬件故障、网络中断、软件错误或其他意外情况时,系统应该能够自动或迅速地进行故障转移、容错处理或恢复,以确保服务的连续性和可用性。简而言之,高可用性是指系统具备在任何时候都能够保持可用状态的能力。

常见的高可用性技术和策略:

  1. 冗余和备份:通过使用冗余的硬件设备、数据备份和存储冗余来保证系统的可用性。
  2. 故障检测和自动切换:使用监控和故障检测机制来及时发现故障,并自动切换到备用设备或节点。
  3. 负载均衡:将流量分布到多个服务器上,以确保系统不会因为某个节点的故障而导致服务中断。
  4. 容错处理:通过使用冗余计算、数据冗余和错误检测纠正技术来处理和修复错误。
  5. 水平扩展和集群:通过增加服务器的数量和节点的数量来提高系统的可用性和性能。

生产上的各种消息队列服务都是分布式部署,那么它们是怎么解决高可用问题的呢?现在为你一一介绍。

高可用方案

在分布式系统中,有多种高可用方案,每种都有其独特的优缺点,如果以节点身份来区分,可以分为一主多从和多主多从。

  1. 主从复制(Master-Slave Replication)

一个主节点处理写入操作,多个从节点用于数据冗余和读取。读写分离,提高了系统的读取性能。

  • 优点:简单易懂,易于实现;读写分离提高了读取性能。
  • 缺点:整体可用性受限于主节点,写入性能受制于主节点;数据同步可能存在延迟。
  1. 多主多从(Multi-Master Replication)

多个节点同时处理写入操作,可以并行处理写入请求;适用于写入负载较高的场景。

  • 优点:高写入性能;故障时系统仍可提供写入服务。
  • 缺点:部署和维护相对复杂;数据一致性难以保证。

如果以节点的读写性来区分,可以分为读写分离和热备份

  1. 读写分离(Read-Write Splitting)

将读操作分流到读库,提高了系统的并发读取能力;适用于读取负载较高的场景。

  • 优点:提高了读取性能;主节点专注于写入操作,减轻了写入压力。
  • 缺点:数据同步可能存在延迟。写入性能仍受制于主节点。
  1. 热备份(Hot Standby)

一个主节点提供读写服务,多个从节点用于数据冗余和在主节点故障时提供故障切换服务;故障切换时,将一个从节点提升为新的主节点。

  • 优点:故障切换相对快速;资源利用率高,主节点专注于提供读写服务。
  • 缺点:读写性能仍受限于主节点;整体可用性受主节点影响。

在实际应用中,可以根据需求组合使用不同的架构形式,例如多主多从结合读写分离,或热备份结合缓存等。

Kafka 的高可用架构

Kafka 的高可用方案是主从复制加热备份。具体来说,每个分区的数据都会被复制到多个 Broker 上,这些副本分布在不同的服务器上,确保在某个 Broker 故障时,其他副本依然可用。以下是 Kafka 的高可用架构演进的主要历程:

Kafka 每个主题的多个分区日志分布式的存储在Kafka集群上,同时为了容错,每个分区都会以副本的方式复制到多个消息代理节点上,其中一个节点会作为主副本,其他的节点会作为内分副本,主副本会负责所有客户端的读写操作,备份副本仅仅从主副本同步数据,当主副本故障时,备份副本中的一个副本会被选择成为新的主副本,因为每个分区的副本中只有主副本接受读写,所以每个服务器都会作为某些分区的主副本,以及灵位一些分区的备份副本,这样Kafka集群所有服务端整体上对客户端是负载均衡的。
---郑奇煌.Kafka技术内幕(第5页).北京:人民邮电出版社,2017

  1. 单一 Broker: 初始阶段,Kafka 是一个单一 Broker 的系统。这意味着所有的数据只有一个副本,如果这个 Broker 发生故障,可能会导致数据丢失和服务中断。
  2. 多副本: 为了提高可靠性,Kafka 引入了多副本机制。每个分区的数据被复制到多个 Broker 上,通常设置为多个副本,例如设置为3或更多。每个副本都位于不同的机器上,确保即使某个 Broker 发生故障,其他副本也能够提供服务。
  3. ISR(In-Sync Replica): Kafka 引入了 ISR 的概念,表示处于同步状态的副本。ISR 中的副本与 leader 副本保持同步,确保在 leader 发生故障时,ISR 中的其他副本可以迅速接替成为新的 leader。这提高了故障切换的速度。
  4. Controller Broker: Kafka 引入了 Controller Broker 的概念,负责协调 Broker 之间的状态,并负责管理分区的 Leader 和 ISR 列表。Controller Broker 是集群中的一个 Broker,负责监控集群状态,并在需要时进行故障转移。
  5. Unclean Leader Election: 在 ISR 中,如果某个副本无法及时同步,可能会导致 ISR 列表缩小,降低可用性。为了在这种情况下继续提供服务,Kafka 引入了 Unclean Leader Election,允许在非同步状态下选择新的 leader。
  6. Rack-aware Replication: 为了提高可用性和容灾能力,Kafka 引入了 Rack-aware Replication,即将副本分布到不同的机架上,防止单个机架故障导致数据不可用。

在Kafka中,一条消息只有被ISR集合的所有副本都运用到本地的日志文件,才会认为消息被成功提交了。任何时刻,只要ISR至少有一个副本是存活的,Kafka就可以保证“一条消息一旦被提交,就不会丢失”。只有已经提交的消息才能被消费者消费,因此消费者不用担心会看到因为主副本失败而丢失的消息。
---郑奇煌.Kafka技术内幕(第11页).北京:人民邮电出版社,2017

Kafka 的高可用架构通过多副本、ISR、Controller Broker 等机制,不断演进和改进,提高了系统的可靠性和容灾能力,确保在发生故障时仍能够提供稳定的服务。

为什么 Kafka 不像 MySQL 那样允许追随者副本对外提供读服务?

  1. 延迟和可用性:Kafka 的设计目标是实时流处理,追求低延迟和高可用性。将追随者副本暴露给外部读取请求会增加系统的复杂性,并可能引入额外的延迟。Kafka 通过提供高吞吐量和低延迟的方式来满足实时处理的需求,而不是通过读写分离的方式来提高读取性能。
  2. 复杂的一致性处理:读写分离有对数据一致性有更高的要求
  3. 场景不适用:读写分离适用于读写速度不对等的场景,MQ 的 Broker 读写速度大致是一样的。

Kafka 在节点选举期间,可能会存在一小段时间内的服务中断,因为只有主节点可以读写客户端的请求,在新主节点切换时,服务是不可用的。但整体来说,Kafka 的设计目标是在高可用性和持久性之间做出权衡,以确保在任何时刻都能够提供服务。

RocketMQ 的高可用架构

Rocket MQ的部署方式是可选的,其中有单主、多主单副本、多节点多副本-异步、多节点多副本-同步,四种部署方式,所以它的高可用架构也随着部署方式的不同而变化。

下面分别介绍这4种部署方式

  1. 单主节点模式

只有一个Broker节点,由这个节点处理全部的读写请求。这种方式风险较大,因为 Broker 只有一个节点,一旦Broker重启或者宕机时,会导致整个服务不可用。不建议线上环境使用, 可以用于本地测试。

  1. 多主节点单副本模式

一个集群内全部部署 Master 角色,不部署Slave 副本,例如2个Master或者3个Master,这种模式的优缺点如下:

  • 优点:配置简单,单个Master宕机或重启维护对应用无影响,在磁盘配置为RAID10时,即使机器宕机不可恢复情况下,由于RAID10磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢),性能最高;
  • 缺点:单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受到影响。
  1. 多节点多副本模式-异步复制

每个Master配置一个Slave,有多组 Master-Slave,HA采用异步复制方式,主备有短暂消息延迟(毫秒级),这种模式的优缺点如下:

  • 优点:即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,同时Master宕机后,消费者仍然可以从Slave消费,而且此过程对应用透明,不需要人工干预,性能同多Master模式几乎一样;
  • 缺点:Master宕机,磁盘损坏情况下会丢失少量消息。
  1. 多节点多副本模式-同步双写

每个Master配置一个Slave,有多对Master-Slave,HA采用同步双写方式,即只有主备都写成功,才向应用返回成功,这种模式的优缺点如下:

  • 优点:数据与服务都无单点故障,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高;
  • 缺点:性能比异步复制模式略低(大约低10%左右),发送单个消息的RT会略高,且目前版本在主节点宕机后,备机不能自动切换为主机。

值得注意的是,跟 Kafka 一样,RocketMQ 的从节点(Slave)一般情况下也是不处理读请求的。RocketMQ 的设计中,读请求主要由主节点(Master)来处理,而从节点的主要任务是同步主节点的消息数据,提供冗余和容错能力。同样的,在节点选举时的可用性方面,RocketMQ 在节点选举期间,可能会经历一些短暂的服务不可用或延迟,拒绝客户端的读写请求。 在节点选举期间,可能会暂时无法处理写请求。这是因为选举过程中,新的主节点被选出,正在迁移状态,可能需要一段时间来完成。 读请求可能会受到一些影响,尤其是在选举期间。读请求有可能需要等待选举完成并新主节点开始正常工作。

Pulsar 的高可用架构

Pulsar 采用多副本的存储方式,每个 Partition 都有多个副本分布在不同的节点上。这样,即使某个节点发生故障,仍然可以从其他副本中获取数据,确保消息的可用性。Pulsar 使用 ZooKeeper 来协调和管理集群中的各个节点。ZooKeeper 负责监控Broker节点的健康状态,协调Leader的选举,以及维护集群的元数据。

需要注意的是,不像 Kafka,Pulsar 的设计允许客户端从任何副本(Leader 或 Follower)读取消息。当客户端发送读请求时,如果该请求到达的节点是 Follower,它也可以处理并提供相应的数据。这种设计模式有助于分担 Leader 节点的负载,提高了读取性能,并允许系统更好地应对大量读取请求。

还有一点就是,在故障切换期间,当主节点(Leader)发生故障并触发新的 Leader 选举时,可能会存在一小段时间内主节点不可用的情况。在这个过渡期间,Pulsar 使用了一种策略来确保写请求的可用性:如果客户端发送写请求,Pulsar 会尽量确保请求被接收和处理。这可能包括将写请求路由到其他节点(Follower 节点),并在新的 Leader 选举完成后将数据同步到新的 Leader。这样,尽管在选举期间可能会存在一些延迟,但系统仍然可以保持写请求的可用性。需要注意的是,虽然在选举期间可能会出现一些短暂的写入延迟,但一旦新的 Leader 选举完成,系统就会恢复正常。这种设计保证了 Pulsar 在主节点发生故障时依然能够提供写请求处理的能力。