当前位置: 首页 > 科技观察

Kafka依靠什么机制来保持高可靠和可用性?

时间:2023-03-14 19:52:59 科技观察

面试大厂,一旦简历上写了Kafka,几乎难免会问一个问题:说说Acks参数对消息持久化的影响?这个Acks参数在Kafka的使用中非常核心和关键,一个参数决定了很多事情。因此,无论是面试还是实际项目使用,这篇文章对Kafka的Acks参数及其背后原理的分析都值得一看。如何保证宕机期间数据不丢失?想要理解Acks参数的含义,首先要理解Kafka的高可用架构原理。比如下图,对于每个Topic,我们可以设置它包含若干个Partition,每个Partition负责存储Topic的一部分数据。然后,在Kafka的Broker集群中,每台机器都存储了一些Partition,其中也存储了Topic的一部分数据。这样Topic的数据就分布存储在了一个Broker集群上。但是有个问题,万一一个KafkaBroker宕机了,这时候上面存储的数据不就丢失了吗?是的,这是一个比较大的问题。分布式系统中数据丢失的问题是首先要解决的,一旦任何一台机器宕机,此时都会导致数据丢失。多副本冗余的高可用机制那么如果分析任何一个分布式系统的原理,比如Zookeeper、Kafka、RedisCluster、Elasticsearch、HDFS等等。其实它们都有自己内部的多副本冗余机制。多副本冗余几乎是任何优秀的分布式系统都应该具备的功能。在Kafka集群中,每个Partition都有多个副本,其中一个副本称为Leader,其他副本称为Follower,如下图所示:如上图所示,假设一个Topic被拆分成3个Partition,即Partition0、Partiton1、Partition2,此时每个Partition都有2份。比如Partition0有一份Leader和一份Follower。Leader和Follower的两份副本分布在不同的机器上。这样的多副本冗余机制可以保证任何一台机器挂了,数据不会完全丢失,因为至少其他机器上还有副本。多副本之间如何同步数据?接下来我们看看多副本之间是如何同步数据的。实际上,任何一个Partition都只有Leader提供读写服务。也就是说,如果一个client向一个Partition写入数据,一般此时是写入到该Partition的Leader副本中。那么Leader副本接收到数据后,Follower副本会不停的向它发送请求,试图拉取最新的数据,拉取到自己的本地,写入磁盘。如下图所示:ISR指的是什么?了解了Partiton的多副本同步数据机制后,可以看看什么是ISR。ISR的全称是“In-SyncReplicas”,即保持同步的副本。这意味着有始终与Leader同步的Follower。大家可以想一想,如果一个follower所在的Broker因为JVMFullGC等问题卡住了,不能及时从leader拉取同步数据,follower的数据会不会落后于leader?所以这个时候,就意味着Follower和Leader不再是同步关系了。但是只要Follower一直及时从Leader同步数据,就可以保证他们是同步关系。所以每个Partition都有一个ISR,这个ISR里面肯定有一个Leader,因为Leader确定数据是最准确的,那么那些和Leader保持同步的Follower也会在ISR里面。Acks参数的含义铺垫了这么多,终于可以进入正题说说Acks参数的含义了。如果不了解前面的拷贝机制、同步机制、ISR机制,那么就无法完全理解Acks参数的含义。这个参数其实决定了很多重要的事情。首先在KafkaProducer中设置Acks参数,即生产者客户端。也就是说,当你向Kafka写入数据时,你可以设置Acks参数。那么这个参数其实有三个常用的值可以设置,分别是:0、1和all。第一个方案是把Acks参数设置为0,也就是说我的KafkaProducer在客户端,只要消息发出去,不管数据有没有落到PartitionLeader上的磁盘上,我不在乎。认为消息发送成功。如果你使用这个设置,那么你一定要注意,你发送的消息可能还在中途。结果PartitionLeader所在的Broker直接挂掉了,结果你的client还以为消息发送成功了,会导致消息丢失。第二个选项是设置Acks=1,也就是说只要PartitionLeader收到消息并写入本地磁盘,就认为成功了,不管它的其他follower是否已经同步了消息。这个设置其实是Kafka的默认设置。请注意!这是默认设置。也就是说,默认情况下,如果忽略Acks参数,只要PartitionLeader写入成功,就认为成功了。但是这里有一个问题。万一PartitionLeader刚收到消息,Follower还没来得及同步过去。结果Leader所在的Broker宕机了,这个时候这条消息也会丢失,因为client已经认为send成功了。.***一种情况是设置Acks=all,也就是说PartitionLeader收到消息后,还必须要求ISR列表中那些与Leader同步的Follower同步消息,才能考虑这条消息它写入成功。如果PartitionLeader刚刚收到消息,而Follower没有收到消息,此时Leaderdown了,那么client会感知到消息没有发送成功,会再次尝试发送消息.这时候Partition2的Follower就有可能成为Leader。此时ISR列表中只有最后一个Follower已经转化为Leader,所以只要新的Leader收到消息,就认为成功了。***思考acks=all可以表示数据不会丢失吗?当然不是,如果你的Partition只有一份,也就是一个Leader,没有Follower,你觉得acks=all有用吗?当然不是是的,因为ISR中只有一个Leader,收到消息就crash了,也会造成数据丢失。因此,这个Acks=all必须与ISR列表中至少2个或更多的replicas一起使用,至少有1个Leader和1个Follower。只有这样才能保证写一条数据的时候,如果收到了两份以上,一定是成功的。这时候数据的任何一个副本都会宕机,数据不会丢失。所以希望大家能够好好的看懂这篇文章。对大家出门面试或者工作使用Kafka都会有很好的帮助。作者:中华狮山中华狮山:十余年BAT架构经验,一线互联网公司技术总监。带领数百人团队开发过亿级大流量高并发系统。多年工作积累的研究手稿和经验总结,现整理成文,一一传授。微信公众号:石山的建筑笔记(ID:shishan100)。