MQ
保证不会丢消息。
把消息复制到多节点,可
所以都会把MQ配置成集群模式,并开启消息复制。
那么消息复制需要解决哪些问题呢?
期望MQ具备高性能、高可用和数据一致性。很多MQ都声明这些特性全部支持,但都有前置条件。
无论采用哪种复制,都需数据被写到多节点后再返回,性能一定不如只写入一个节点。
需要写入节点越多,可用性和数据可靠性越好,但写性能就越低。 不过,复制对消费的性能影响不大,不管采用哪种复制方式,消费消息时,都只选择多副本中一个的节点去读,和单节点消费无异。
MQ对数据一致性要求包括:
若要确保数据一致性,必须采用主从复制。
主从模式下,数据先写到主节点,从节点只从主节点上复制,若出现主从数据不一致,须以主节点数据为准。
这里的主节点并非不可变,在很多复制实现中,当主节点出现问题,其他节点可通过选举,变成主节点。只要保证,在任一时刻,集群的主节点数不能超过1个,即可确保数据一致性。
须采用主从复制,高可用需解决的就是,当某个主节点宕机,尽快再选个主节点来继位。
下面看通用的分布式系统设计实现方案:
使用三方服务管理这些节点,发现某主节点宕机,由管理服务指定一个新的主节点。 但引入服务会带来一系列问题,比如管理服务本身的高可用、数据一致性如何保证?就如 redis 哨兵。
有的MQ选择自选举,由存活节点投票选举新的主节点。
大部分复制实践,都不会选择把消息写入全部副本再返回确认,因为这样虽可保证数据一致性,但一旦这些副本中有任一宕机,写入就会卡死。 若只把消息写入部分副本就认为写入成功并返回确认,即可避免卡死且性能好些。 那写入多少个副本算写入成功?假设集群采用1主2从3副本:
不同MQ选择不同复制实现,有各自优缺点。
RocketMQ复制的基本单位是Broker,服务端进程。采用主从复制,通常配置成一主一从,也支持一主多从。
消息先发送到主节点,就返回“写入成功”,然后再把消息异步复制到从节点。
消息同步双写到主从节点,主从都写成功,才返回“写入成功”。
这两种方式区别是 写入多少副本再返回写入成功 :
若在返回“写入成功”前,需要写入的副本数不够多,就会丢消息。
那RocketMQ采用异步复制会不会丢消息?不会。
RocketMQ的Broker主从关系通过配置固定,不支持动态切换。
若主节点宕机,生产者就不能再生产消息,消费者可自动切换到从节点继续消费。 这时,即使有一些消息没来得及复制到从节点,这些消息依然躺在主节点磁盘,除非主节点磁盘坏了,否则等主节点重新恢复服务,这些消息依然可继续复制到从节点,也可继续消费,不会丢消息,消息顺序也没问题。
这种主从复制方式,通过牺牲可用性,得到较好的性能和数据一致性。
一对主从节点可用性不行,那就多对。
由于topic层无法保证严格顺序,必须指定队列发消息,对任一队列,一定是落在一组特定主从节点,若该主节点宕机,其他主节点无法替代这主节点,否则就无法保证严格顺序。 因此这种复制模式的严格顺序和高可用只能选其一。
2018年底引入Deldger,一种全新复制方式。
Dledger在写入消息时,要求至少消息复制到半数以上节点后,才给客户端返回写成功,且支持选举动态切换主节点。
3节点为例。
复制的基本单位是分区。每个分区的几个副本间构成一个小复制集群。 Broker只是这些分区副本的容器,所以Kafka的Broker不分主从。
分区的多个副本中采用一主多从。 写入消息时,异步复制。消息在写到主节点后,并不会马上返回写入成功,而是等待足够多节点都复制成功后再返回。 “足够多”由用户定。对应:ISR(In Sync Replicas),“保持数据同步的副本”。ISR的数量可配,ISR中包含主节点。
Kafka使用ZooKeeper监控每个分区的多节点,发现某分区主节点宕机:
默认如果所有ISR宕机,分区就无法提供服务。也可以选择配置成让分区继续提供服务,这样只要有一个节点活,就可提供服务,代价是无法保证数据一致性,会丢消息。
Kafka的这种高度可配置的复制方式
没有完美复制方案,要根据业务需求,评估高性能、高可用和一致性。