不同的消息队列实现方式不同,保证高可用的方式也不同,这里我主要列举出比较有代表的两类:
RabbitMQ
RabbitMQ 基于主从实现高可用,包含以下三种模式:
- 单机模式
- 普通集群模式
- 镜像集群模式
单机模式:一般企业不会用到,平时自己本地测试可以搞一下。
普通集群模式:多台机器上部署多个 RabbitMQ 实例,每台机器部署启动一个。每个实例包含独立的队列数据以及同步的元数据,通过元数据可以找到具体队列。
消费者连接其中一个实例,消费 MQ 时可能产生两种结果:
- 要消费的数据就在当前实例,直接消费
- 要消费的数据不在当前实例,通过元数据找到对应队列,拉过来消费
这种模式实际很糟糕,只能提高吞吐量,不具备高可用。某个实例宕机,该实例上所有 MQ 就会丢失,即使配置了持久化,也必须等实例恢复后才可以正常服务。
镜像集群模式:和普通集群不同,镜像集群模式所有实例不但同步元数据,队列数据也同步。每个 RabbitMQ 节点包含队列的完整镜像。每次写数据到 MQ 都会同步到所有的实例。
这种模式虽然保证了高可用,但资源浪费是巨大的。首先每次读和写数据需要同步到所有节点,网络带宽浪费严重,其次每个节点包含完整的数据,数据量很大时,很可能造成所有节点都内存不足,无法线性扩展。
Kafka
Kafka 基于分布式实现高可用:它会划分为多个 broker,每个 borker 保存一部分 topic 分割的 partition 数据。也就是说,每个 topic 的数据不全在一台实例上,而是分布在多台实例上,每个实例保存的数据各不相同。
broker:缓存代理,Kafka集群中的一台或多台服务器统称broker
topic:逻辑上的概念,可以把它看做一类消息的聚合。
partition:topic物理上的分组,每个Partition是一个有序队列。、
ps:上图 Broker 应为 Brokers,每个虚线方块中的一个小方块表示一个 Broker。
在上面的基础上,每个 partition 又引入多副本机制,分为 leader 和 follower。每个 leader 包含多个 follower,每次读、写数据都在 leader 上进行,follower 从 leader 上同步数据。这里只读写 leader 的原因在于简化数据一致性问题。
不同 Topic 划分的 partition 主从可以在同一个 Broker 上:
- 写数据时,生产者将数据写到 leader,其它 follower 主动从 leader pull 数据。一旦所有 follower 同步好数据后,就会发送 ack 给 leader,leader 收到所有 follower 的 ack 之后,返回生产者写成功。
- 消费时只会从 leader 消费,follower 都同步成功后返回消费成功
这样做的好处在于,单个实例的宕机不会影响整体服务。如果宕机的是 follower,leader 还在,不会有影响。如果宕机的是 leader,follower 上还有数据,此时会重新选举一个新的 leader ,后续操作读写那个 leader 即可。