前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文理解消息队列如何保证高可用

一文理解消息队列如何保证高可用

作者头像
全菜工程师小辉
发布2021-06-25 21:02:15
1K0
发布2021-06-25 21:02:15
举报
文章被收录于专栏:后端开发你必须学会的干货

之前博客《一文理解为什么需要使用消息队列》提到过,系统引入消息队列后,需要考虑如何保证消息队列的高可用。

本篇文章将围绕几个常见的消息队列中间件(RabbitMQ,RocketMQ,Kafka)进行逐个讲解。

RabbitMQ

RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。

单机模式

单机模式,一般是开发者本地启动调试使用,不会应用到生产环境。

普通集群模式

普通集群模式下,在多台机器上分别启动一个RabbitMQ实例。新创建的queue,只会放在其中一个RabbitMQ实例上,但是每个实例都同步queue的元数据(元数据是queue的一些配置信息,例如通过元数据可以找到queue所在实例)。在消费者进行消费的时候,如果连接到的实例没有指定的queue,那么这个实例会从queue所在实例上拉取数据过来。

这种模式下,要么消费者每次随机连接一个实例然后拉取数据,要么固定连接指定queue所在实例消费数据,前者有数据拉取的开销,后者可能会导致单实例性能瓶颈。

缺点:

  1. 性能开销大,不同节点间的数据传输,可能会导致网络带宽压力和消耗很重。
  2. 如果某个queue实例宕机,会导致节点接下来其他实例就无法从那个实例拉取消息。即时开启了消息持久化,在该节点重启的过程中,对外服务也是中断。

普通集群模式相对单机模式,提升了消费速度,提高了吞吐量。

镜像集群模式

这种模式下RabbitMQ才能实现高可用。跟普通集群模式不一样的是,在镜像集群模式下,创建的queue,无论元数据还是queue里的消息都会完整存在于多个实例上。

这样就可以保证任何一个节点宕机后,其他节点还包含了这个queue的完整数据,consumer可以到其他的节点上去消费数据。

缺点:

  1. 性能开销大,消息需要同步到多个节点,导致网络带宽压力和消耗很重。
  2. 节点没有拓展性。如果某个queue负载过重,即使添加新的RabbitMQ节点,也需要包含这个queue的所有数据。

注:

  1. 指定queue的消息同时存在节点的数量是可以通过RabbitMQ控制台配置进行设置。

镜像集群模式相对普通集群模式,提升了可用性,但对吞吐量没有改善。

RocketMQ

RocketMQ的集群方式分为:

  1. 多Master模式
  2. 多Master多Slave异步模式(一般生产环境使用)
  3. 多Master多Slave同步模式(对数据可靠性要求高时使用)

RocketMQ中由NameServer集群、Broker 集群、Producer 集群和Consumer集群组成。

  1. NameServer: 提供轻量级的服务发现和路由。每个NameServer记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展。
  2. Broker: 通过提供轻量级的 Topic 和 Queue 机制来处理消息存储,同时支持推(push)和拉(pull)模式以及主从结构的容错机制。
  3. Producer:生产者,产生消息的实例,拥有相同Producer Group的Producer组成一个集群。
  4. Consumer:消费者,接收消息进行消费的实例,拥有相同Consumer Group的Consumer组成一个集群。

RocketMQ是通过Broker主从机制来实现高可用的。相同Broker名称,不同Brokerid的机器组成一个Broker组,BrokerId=0表明这个Broker是Master,BrokerId>0表明这个Broker是Slave。

消息生产的高可用:创建Topic时,把Topic的多个message queue创建在多个broker组上。这样当一个Broker组的Master不可用后,Producer仍然可以给其他组的Master发送消息。

消息消费的高可用:Consumer并不能配置从Master读还是Slave读。当Master不可用或者繁忙的时候,Consumer会被自动切换到从Slave读。这样当Master出现故障后,Consumer仍然可以从Slave读,保证了消息消费的高可用。

可以理解为:Rocketmq是通过多个Master实现写入容灾,通过主从实现读取容灾。

上图中,Broker Master1和Broker Slave1 是主从结构,实例之间会进行数据同步。同时每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer中。

Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Broker Master建立长连接,且定时向Broker发送心跳。Producer只能将消息发送到Broker Master;是Consumer则不一样,它同时和提供Topic服务的Master和Slave建立长连接,既可以从Broker Master订阅消息,也可以从Broker Slave订阅消息。

在RocketMQ里面,1台机器要么是Master,要么是Slave,这在初始的机器配置里面就确定了。其中Master的Broker id = 0,Slave的Broker id > 0。有点类似于MySQL的主从概念,Master挂了以后,Slave仍然可以提供读服务,但是由于有多主的存在,当一个Master挂了以后,可以写到其他的Master上。

2.1 多Master模式

只有Master,无Slave。某个实例挂了,该实例在重启前未被消费的消息无法被消费。

优点:配置简单,性能最高。

缺点:单台机器重启或宕机期间,该机器下未被消费的消息在机器恢复前不可订阅,影响消息实时性。

2.2. 多Master多Slave异步模式

每个Master配一个Slave,有多对Master-Slave,集群采用异步复制方式,主备有短暂消息延迟,毫秒级

优点:性能同多Master几乎一样,实时性高,主备间切换对应用透明,不需人工干预。

缺点:Master宕机或磁盘损坏时会有少量消息丢失。

2.3. 多Master多Slave同步模式

每个Master配一个Slave,有多对Master-Slave,集群采用同步双写方式,主备都写成功,才向应用返回成功

优点:服务可用性与数据可用性非常高。

缺点:性能比异步集群略低(大约低10%)。

Kafka

kafka 0.8以前,是没有HA机制的,就是任何一个Broker宕机了,那个Broker上的Partition就没法写也没法读,无法实现高可用性。

Kafka 0.8版本以后,增加了replica副本机制,从而实现了Kafka的高可用性。

基础知识

如果对Kafka不了解的话,可以先看这篇博客《一文快速了解Kafka》。

消息传递同步策略

Producer在发布消息到某个Partition时,先通过ZooKeeper找到该Partition的Leader,然后无论该Topic的Replication Factor为多少,Producer只将该消息发送到该Partition的Leader。Leader会将该消息写入其本地Log。每个Follower都从Leader pull数据。这种方式上,Follower存储的数据顺序与Leader保持一致。Follower在收到该消息并写入其Log后,向Leader发送ACK。当Leader收到了ISR中的所有Replica的ACK,该消息就被认为已经commit了,Leader将增加HW并且向Producer发送ACK。

为了提高性能,每个Follower在接收到数据后就立马向Leader发送ACK,而非等到数据写入Log中。因此,对于已经commit的消息,Kafka只能保证它被存于多个Replica的内存中,而不能保证它们被持久化到磁盘中,也就不能完全保证异常发生后该条消息一定能被Consumer消费。

Consumer读消息也是从Leader读取,只有被commit过的消息才会暴露给Consumer。

Kafka Replication的数据流如下图所示:

Kafka高可用机制

每个Partition的数据都会同步到其他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。只能读写leader?很简单,要是你可以随意读写每个follower,那么就要care数据一致性的问题,系统复杂度太高,很容易出问题。kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性。

kafka的这种机制,就有所谓的高可用性了,因为如果某个broker宕机了,也没事儿,因为那个broker上面的partition在其他机器上都有副本的,那么此时会重新选举一个新的leader出来,大家继续读写那个新的leader即可。这就有所谓的高可用性了。

  1. 写过程

写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)

  1. 读过程

消费的时候,只会从Leader去读,但是只有当一个消息已经被所有follower都同步成功并返回ack的时候,这个消息才能够被消费者读到。

对比RocketMQ与Kafka的架构区别

  1. namesrv VS zk(不考虑Kafka2.8的Raft元数据模式)

Kafka通过Zookeeper来进行协调,而RocketMq通过自身的namesrv进行协调:

  • Kafka在具备选举功能,在Kafka里面,Master/Slave的选举,有2步:第1步,先通过ZK在所有机器中,选举出一个KafkaController;第2步,再由这个Controller,决定每个partition的Master是谁,Slave是谁。因为有了选举功能,所以kafka某个partition的master挂了,该partition对应的某个slave会升级为主对外提供服务。
  • RocketMQ不具备选举,Master/Slave的角色也是固定的。当一个Master挂了之后,你可以写到其他Master上,但不能让一个Slave切换成Master。那么rocketMq是如何实现高可用的呢,其实很简单,rocketMq的所有broker节点的角色都是一样,上面分配的topic和对应的queue的数量也是一样的,Mq只能保证当一个broker挂了,把原本写到这个broker的请求迁移到其他broker上面,而并不是这个broker对应的slave升级为主。
  1. 吞吐量的对比
  • Kafka在消息存储过程中会根据Partition的数量创建物理文件,例如创建一个topic并指定了3个Partition,那么就会有3个物理文件。
  • RocketMQ使用commitLog进行消息存储(顺序写,随机读),相当于只有一个物理文件。

Kafka的多文件并发写入相对RocketMQ的单文件写入,Kafka的性能要好很多。但Kafka的大量文件存储会导致一个问题:当Broker中包含Partition特别多的时候,磁盘的访问会发生很大的瓶颈,毕竟单个文件看着是append操作,但是多个文件之间必然会导致磁盘的寻道。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全菜工程师小辉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • RabbitMQ
    • 单机模式
      • 普通集群模式
        • 镜像集群模式
        • RocketMQ
        • Kafka
          • 基础知识
            • 消息传递同步策略
              • Kafka高可用机制
                • 对比RocketMQ与Kafka的架构区别
                相关产品与服务
                消息队列 CMQ 版
                消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档