/brokers/topics/[topic]
/brokers/topics/[topic]/partitions/[partitionId]/state
/brokers/ids/[0...N]
/controller_epoch
,每当controller发生变更,对应的epoch就会递增/controller
,存储controller所在的broker节点信息/consumers/[groupId]/offsets/[topic]/[partitionId]
,用来跟踪每个Consumer所消费的分区最大offset,此节点为持久化节点admin/delete_topics
,需要删除的Topic信息/config/topics/[topic_name]
Kafka利用主题Topic和分区Partition的概念对消息数据进行组织,每个Topic可以有多个分区,而每个分区可以设置多个副本保存在集群的多个Broker节点上。
Kafka引入分区的概念,主要是为了利用分布式系统中的多节点来提升Kafka集群的性能和可扩展性。可以根据Topic的消息读写压力,可以增加或减少分区数来实现动态扩缩容的目的。而分区的多副本机制,则保障了集群的高可用性,这些副本被保存在不同的Broker节点上,副本集合是由一个Leader副本和其他作为Follower副本组合而成,需要注意的是,生产者发送消息和消费者消费消息只与分区的Leader副本交互,Follower副本则不处理任何请求,只负责从Leader副本拉取并同步消息数据。当分区Leader副本失效(Broker宕机等),会从Leader副本中选举出新的Leader副本。
思考:为什么Kafka在设计上只允许Leader副本对外提供服务,而不采用经典的读写分离策略(即允许消费者从Follower副本读取消息)?
读写分离策略在数据库读写应用上较为常见,能够有效解决数据库集群负载压力,但是这适用的场景是读多写少,如果是读少写多的场景,那么主从复制会存在较大延迟,并且数据库的主要压力会几种在master节点,读写分离并不能分摊这部分负载。对Kafka而言,作为消息引擎在大部分场景在是读写对等的,所以读写分离策略的收益并不大,反而会增加设计的复杂度。Kafka的一个Topic的多个分区Leader副本被分散在多个Broker节点上,这已经是负载均衡的一个设计。
为了保证broker节点宕机时,由该节点管理的topic分区仍然可用,Kafka一般会为topic分区分配多个副本,这些副本成为该Topic分区的Assigned Replicas,简称AR副本集合。
ISR副本集合是指由一个topic分区名下的In-Sync-Replica副本构成的集合。ISR集合是AR的子集,如果AR集合中的副本能够同时满足下列条件,则有资格加入到ISR集合中:
replica.lag.time.max.ms
的值,默认是10sISR集合由每个topic分区的Leader副本进行维护,Follower副本在启动后会从Leader副本同步数据消息,当Follower赶上Leader,offset在一定范围后,该Follower副本就有资格被添加进该Topic分区的ISR集合中,ISR集合的个数也由参数控制。与之相反,当Follower副本因一些原因(broker宕机,网络抖动,zookeeper连接异常等)导致消息offset滞后Leader副本超过阈值,那么会被Leader副本从ISR集合中移除。
当Leader副本失效时,会优先从ISR副本集合中竞选出一个成为新的Leader。
思考:为什么采取ISR机制而非一致性协议来保证数据的一致性?
当面临需要维护多个副本的数据一致性时,一般会考虑引入Paxos、Raft这些一致性协议。Kafka不采用一致性协议而采用ISR的原因分析:
HW和LEO是消费位移offset的两个重要指标,HW是High WaterMark,LEO是Log End Offset。
对于每个副本来说,LEO是指向当前最大消费位移offset的下一位的值,当消费者消费完成后,Leader副本的LEO递增,之后Follower副本从Leader副本拉取消息时更新LEO。
HW是有每个分区的Leader副本进行维护,消费者在消费当前topic分区时只能看到HW位置之前的消息。消费者消费完消息之后提交给Leader副本会携带一个acks参数,用于指定是否需要服务端对本次请求进行确认,以及在什么情况下确认。如果acks=-1,则表示需要ISR集合中的所有副本完成对当前消息同步之后,Leader副本才认为此消息被成功记录到集群,此时Leader副本才会递增HW值,一般而言acks不需要设置所有副本都需要完成同步。
思考:HW有什么作用,是怎么帮助Kafka完成副本同步的?
在Kafka中,HW的作用主要有两个:
虽然所有副本都会保存一组HW和LEO值,但是Kafka是通过Leader副本的HW来定义其所在分区HW的,即分区的HW就是其Leader副本的HW,除此之外,Leader副本所在的Broker还保存了所有Follower副本的LEO值,这也成为远程副本(Remote Replica)。先从HW和LEO的更新说起:
Leader副本处理生产者请求的逻辑:
Leader副本处理Follower副本拉取消息的逻辑:
Follower副本从Leader副本拉取消息的处理逻辑:
除了利用HW和LEO实现副本同步之外,为了避免Leader和Follower在HW更新时间上错配而导致的数据丢失或数据不一致问题,引入了Leader Epoch的概念:
所以在当Broker宕机恢复时会先根据Leader Epoch判断是否要进行日志截断操作。
对Kafka的整体架构做了一个简单的梳理,Kafka作为一个消息引擎系统,通过主题多分区和零拷贝实现了高并发和大吞吐量,并且将数据以日志形式持久化到磁盘保障消息不丢失。分区多副本机制、ISR机制和高水位HW保障了Kafka集群的高可用性。之后会对Kafka的一些核心模块源码解读,深入理解Kafka是如何运行和管理的。
贝实现了高并发和大吞吐量,并且将数据以日志形式持久化到磁盘保障消息不丢失。分区多副本机制、ISR机制和高水位HW保障了Kafka集群的高可用性。之后会对Kafka的一些核心模块源码解读,深入理解Kafka是如何运行和管理的。