问题:producer将消息提交给kafka集群后,是在什么时候完成消息删除的?
答:producer将消息提交给kafka集群后,kafka集群会向producer发送ack信息。然后producer会依据配置的acks判断是否删除消息。
acks属性可以配置的值是:all、-1、0、1。
以下为猜测内容,没看过具体源码。
producer将消息发给leader的请求,会直接返回一个ack=0的通知。
leader将消息完成本地持久化后,会向producer发送一个ack=1的通知。
leader判断所有follower副本也完成持久化后,会再次向producer发送一个ack=-1的通知。
producer删除数据的时机就是以上三个时刻,依据acks的配置执行删除。
当producer在指定时间没有收到ack的话,会启动重新发送流程。
问题:producer向kafka集群提交的过程中,集群是如何保证一致性的?
答:broker启动配置中有一项:min.insync.replicas,是配置多少副本完成持久化后,可以向producer发送ack=-1通知的。
合理配置该属性可以优化以下两方面的内容:
当副本数比较多时,可以降低集群一致性要求,加快消息的传输速度。
当出现极端情况,没有足够的副本时,通知生产者集群不能接收数据,防止出现数据丢失。
但需要注意可能出现的问题:
如果你只有2个副本,而这个值配置了3会造成producer生产的消息一直推送不成功。提示副本数不够。
问题:acks=-1时判断的副本是所有副本吗?
答:不是,是所有在ISR中的副本。这里涉及到两个在kafka中的概念ISR和OSR。
ISR,in sync replicas,是可以同步副本集合,就是可以及时从leader副本复制信息的副本,包括leader自己。
及时有两种判断方式:数据差距条数和数据同步延迟时间,超过阈值就会将副本放到OSR中。
数据同步延迟时间,是比较leader数据接收时间和follower请求leader获取数据的时间。因为集群内部各副本之间的同步是通过follower向leader发送fetch请求完成的。
该值得配置项是replica.lag.time.max.ms,单位是ms,默认值是10000。
OSR,OSR是out sync replicas,可以理解为不在ISR中的副本。
ISR在kafka中是一个重要的集合,leader也是从ISR中选出来的。
可以通过在服务器中查看主题信息看到。
命令如下:
bin/kafka-topics.sh --zookeeper zookeeper:2181 --describe --topic test
响应结果如下:

其中第一行是Topic信息,包括名称、ID和分区数量。
下面的表格信息,写的是各个分区的情况。
Leader表明的是,leader分区所在的broker。
Replicas表明各分区副本所在的broker。
ISR,in sync replicas,可以同步副本集合。
问题:可不可创建超过broker数量的副本数?
答:建议小于等于broker数量,保证每一个broker有最多一个副本,存在多个没意义。
问题:数据是如何从leader同步到follower的?
答:follower会定时从leader中同步(FETCH)数据。
这里需要先说明3个概念,LEO、HE、Remote LEO。
partition中的数据储存可以理解为一个数组+三个指针。一个是可以开始写的位置,一个是可以读的最后位置。当然还有当前位置。
LEO,是last end offset,是最后一个结束的偏移量,也就是可以继续写的位置,这个每写入一个数据会自动+1。
HW,是high watermark,就是可以读的最后位置。
Remote LEO,就是在leader中储存的follower中的LEO。
同步数据的过程按照如下进行的:
follower先向leader发出一个FETCH请求,请求中会带自己的LEO。
leader接到请求后,会完成两个操作。
其一是完成HW的更新,将follower的LEO储存在Remote LEO中,然后比较自己的HW和Remote LEO,较小的就是leader的HW。
其二是完成数据同步,比较follower的LEO和leader自己的LEO,如果一致表明数据一致,如果不一致将多的一部分数据返回给follower。
follower接到响应后,会完成两个操作。
其一是完成数据同步,这里有个问题,如果leader的HW比follower的LEO小,follower的LEO会变更为leader的HW,相应的数据会视为丢失。
其二是完成HW的同步。
按照上述流程不断重复,follower每fetch一次都会同步一次数据和HW,但HW的完全同步需要在没有数据更新的下一次同步过程中完成。
问题:数据同步的过程中可能出现那些问题?新版是如何解决的?
答:可能出现数据丢失和数据不一致。
造成的原因有两部分:
其一是follower成为leader后,会将LEO同步为自己的HW。
其二是当原先的leader重启,变成follower后同步数据,会将LEO和HW设定为新leader的HW。
核心问题都是由于follower被选举成leader时会将LEO和HW设定为一致造成的。如果HW未及时同步,会失去HW和LEO之间的数据。
解决方案是在新版本中,加入了epoch的概念。
在新版本中,follower被选举成leader时不会将LEO和HW设定为一致,而是将HW和LEO设定为一致,并记录纪元信息:迭代次数和迭代的起始位置。
原先的leader重新启动成为follower同步数据时不是直接将LEO和HW设为一致,而是先同步纪元信息,再判断比自己当前纪元大一的纪元的起始位置,判断自己的LEO和该起始位置,将二者的较小值设定为HW和LEO。
但该解决方案仍然存在数据不一致的可能性。
问题:kafka对消息处理的承诺有那些?
答:至少一次,最多一次,精确一次。
至少一次是表明消息保证消息一定可以从生产者传递到kafka集群并被持久化,但不能保证只传递了一次。也就是说保证数据不丢失,但不能保证不重复。
需要通过acks=all,副本数量大于2,最小同步部分数量大于2来实现的。
最多一次是表明消息从生产者发出了,但kafka不一定接到或者能够及时完成持久化。也就是数据一定不重复,但不能保证数据不丢失。
设定acks=0。保证只发送一次。
精确一次在至少一次的基础上保证消息只消费了一次。
需要配合业务实现消息的幂等性。
问题:kafka为保证幂等性做了那些工作?是否完全保证幂等性?
答:kafka在没一个消息中存放了两个值组成的key,来保证该消息只持久化一次。但并不能保证一定幂等性。
kafka在producer提供了两个参数:ProducerId和Sequence Number。
ProducerId是没有producer启动的时候,kafka提供的。
Sequence Number是针对每一个分区的单调自增的值。
不能完全保证幂等性。
首先是这个是针对消息的,如果数据出现重复处理不了。
其次当出现重分区时,消息可能发向不同的分区。
最后是生产端重启,producerId会更新。
幂等性配置参数是:enable.idempotence,配置端是生产者,默认是true,开启。
问题:kafka说的可以保证分区中的数据有序,是如何做到的?
答:目前是基于Sequence Number和max.in.flight.requests.per.connection。
broker持久化的时候是按照SeqNum来完成持久化的,也就是说如果seqNum是1的消息没有完成持久化,seqNum是2的值即使接到也不会完成持久化,一定需要等消息1不断重试到完成持久化才可以继续完成消息2的持久化。
这里涉及到两个流程,一个是producer发送消息,一个是broker持久化消息。
producer发送消息,先将消息放到发送队列,满足发送条件后启动send发送,然后发送到网络连接器,在网络连机器上也可以缓存发送的消息,缓存数量是max.in.flight.requests.per.connection属性指定的。网络连接器会按照顺序逐个发送,如果发送失败会不断重试到发送成功,之后再继续发送下一条数据。
broker持久化也是按照顺序持久化的,是为了加快读取速度。
其实数据从硬盘中读取和从内存中读取耗费的时间差距不大,多出来的时间主要是寻址时间。
如果不是顺序持久化,而是持久化后再排序,这个储存会导致页裂。