kafka使用zookeeper来维护成员信息。没个broker都有唯一的id,这个标识可以在配置文件中配置,在broker启动的时候,它会创建临时的节点把id注册到zookeeper中,当有其他broker加入集群中时,会判断是否已经有这个id,如果有相同的id则会注册失败。
当broker宕机的时候,broker创建的临时节点会从zookeeper中移除,其它的kafka组件也都会收到这个消息。
当一台broker宕机之后,如果启动另外一台具有相同id的broker,那么它会拥有与旧broker相同的分区和主题,因为虽然它的节点消失了,但是它的id会继续存在其它数据结构中。
控制器其实就是一个broker,不过除了正常功能外,还负责分区首领的选举。当第一个broker在zookeeper创建临时节点让自己成为控制器后,其它的broker也都会尝试去创建这个节点,但是都会失败,失败时候,它们会监听这个节点。
如果控制器宕机,那么其它节点会接到这个通知,并且尝试让自己成为新的控制器,这时候会重复上面的动作。
当控制器发现有broker离开之后,如果首领分区正好在这个broker上,那么控制器会遍历这些分区,并确定新的首领分区,然后将该消息发送给其它broker,之后首领分区开始处理生产者跟消费者的请求。
当控制器发现有新的broker加入后,他会检查该broker是否有分区副本,如果有,那么他会将消息发送给其它broker,之后新的broker就会从首领分区处复制消息。
kafka就是采用复制方式,将消息从首领分区复制到跟随者副本中。
每个分区都有一个首领副本,所有生产者与消费者请求都会由这个首领去处理
除了首领分区之外的副本都是跟随者。跟随者除了复制消息,不处理来自客户端的请求,当发现首领副本宕机后,他会成为新的首领。
跟随者为了与首领保持一致,跟随者会发送请求给首领,首领将消息响应给跟随者,响应中就包含偏移量。一个跟随者如果发送了1、2、3请求,在收到3的响应之前,他是不会发送第四个请求的,当它发送第四个请求的时候,首领就知道它已经接收到了前面的响应。通过查看跟随者的最新偏移量,首领就会知道没个跟随者复制的进度。如果跟随者在10s内没有发送消息,或者10s内没有请求最新的数据,那么就会认为它是不同步的。
能持续获得最新的副本叫同步副本,当领导者宕机之后,只有同步副本才有可能成为首领。
除了首领副本,没个分区都有一个优先副本,在创建主题时候,需要在broker中间进行均衡,因此我们希望在首领副本选完之后,broker是均衡处理请求。当首领副本宕机之后,如果优先副本也是同步副本,那么他会优先成为首领。
acks这个配置参数,该参数指定了需要多少个 broker确认才可以认为一个消息写入是成功的。不同的配置对“写入成功”的界定是不一样的,如果acks=1,那么只要首领收到消息就认为写入成功;如果acks=all,那么需要所有同步副本收到消息才算写入成功; 如果 acks=0, 那么生产者在把消息发出去之后, 完全不需要等待 broker的响应。
客户端发送请求,向 broker请求主题分区里具有特定偏移量的消息,如果设置了broker放回数据的下限,那么broker就会等到有足够的数据之后才会放回响应给消费者,当然如果一直没有凑够,但是到了设置的最大等待时间,那么也会返回响应。
并不是所有保存在首领分区上的数据都会被消费者读取到,大部分客户端读取的都是写入所有同步副本的消息。
如果消息只存在首领分区上,还没来得及复制之前就宕机了,那么它新选举出来的首领是不会包含这些信息的,这时候就会发生数据丢失,也会造成数据不一致性。
没个首领都会维护一个同步列表即ISR,这个列表包含了所有与它同步的分区,每次写入数据,只有该列表中所有的分区都写入,这条消息才会被消费者读到。ISR是动态的,如果跟随者被认为不能跟上领导者,那么他将会被移除,直到认为它又有资格,则会将它重新加入,具体的算法策略就不去再研究了。
kafka会根据设置的失效时间及最大容量去清除数据。