19 Kafka ack 有哪几种 ?生产选择哪个?
首先这个acks参数,是在KafkaProducer,也就是生产者客户端里设置的
也就是说,你往kafka写数据的时候,就可以来设置这个acks参数。然后这个参数实际上有三种常见的值可以设置,分别是:0、1 和 all。
第一种选择是把acks参数设置为0,意思就是我的KafkaProducer在客户端,只要把消息发送出去,不管那条数据有没有在哪怕Partition Leader上落到磁盘,我就不管他了,直接就认为这个消息发送成功了。
如果你采用这种设置的话,那么你必须注意的一点是,可能你发送出去的消息还在半路。结果呢,Partition Leader所在Broker就直接挂了,然后结果你的客户端还认为消息发送成功了,此时就会导致这条消息就丢失了。
第二种选择是设置 acks = 1,意思就是说只要Partition Leader接收到消息而且写入本地磁盘了,就认为成功了,不管他其他的Follower有没有同步过去这条消息了。
这种设置其实是kafka默认的设置,大家请注意,划重点!这是默认的设置
也就是说,默认情况下,你要是不管acks这个参数,只要Partition Leader写成功就算成功。
但是这里有一个问题,万一Partition Leader刚刚接收到消息,Follower还没来得及同步过去,结果Leader所在的broker宕机了,此时也会导致这条消息丢失,因为人家客户端已经认为发送成功了。
最后一种情况,就是设置acks=all,这个意思就是说,Partition Leader接收到消息之后,还必须要求ISR列表里跟Leader保持同步的那些Follower都要把消息同步过去,才能认为这条消息是写入成功了。
如果说Partition Leader刚接收到了消息,但是结果Follower没有收到消息,此时Leader宕机了,那么客户端会感知到这个消息没发送成功,他会重试再次发送消息过去。
此时可能Partition 2的Follower变成Leader了,此时ISR列表里只有最新的这个Follower转变成的Leader了,那么只要这个新的Leader接收消息就算成功了。
acks=all 就可以代表数据一定不会丢失了吗?
当然不是,如果你的Partition只有一个副本,也就是一个Leader,任何Follower都没有,你认为acks=all有用吗?
当然没用了,因为ISR里就一个Leader,他接收完消息后宕机,也会导致数据丢失。
所以说,这个acks=all,必须跟ISR列表里至少有2个以上的副本配合使用,起码是有一个Leader和一个Follower才可以。
这样才能保证说写一条数据过去,一定是2个以上的副本都收到了才算是成功,此时任何一个副本宕机,不会导致数据丢失。
---------------------
20 kafka offset 有绝对和相对的说法吗?请解释一下
消息集中的每条消息都会被分配一个相对偏移量,每一批消息集中消息的相对偏移量都从0开始:第一批消息:[0,1,2,3]。第二批消息:[0,1,2]。
消息集中的每条消息有三个部分组成:偏移量,大小,消息内容。消息内容包含:键值,键值的长度,校验值等数据(Record)。
kafka服务端在存储消息时,会为每条消息都指定一个唯一的偏移量。同一个分区的所有日志分段中消息的偏移量从0开始不断递增,不同分区的偏移量直接没关系。这也就是kafka只能保证同一个分区的消息有序性,不能保证跨分区消息的有序性。
客户端创建消息集中每条消息的偏移量,都是相对于本批次消息集的偏移量,每批消息的偏移量都是从0开始,但是这个偏移量不能直接存储在日志文件中。在服务端要对此偏移量进行转换,计算消息的偏移量时,采用下一个偏移量(nextOffsetMetadata)的值来替换消息中的相对偏移量,这时消息中偏移量就是保存在文件中的绝对偏移量。
服务端将每个分区的消息追加到日志中是以日志分段为单位的。日志分段中数据文件的大小的阀值1G,数据文件中存储的消息达到阀值后会创建一个新的日志分段文件来存储信息,分区的消息总是追加到最新的日志分段中,也就是说,一个分区的日志文件,在任何时刻都只有一个活动的日志分段。每个日志分段都有一个基准偏移量,在一个日志分段中是固定值,用它来计算出每条消息在当前这个日志分段中的绝对偏移量,最后把消息写到日志分段中。更新日志的下一个偏移量的值(用来设置下一个消息的绝对偏移量),满足条件时调用flush()把消息刷写到磁盘。
下一个偏移量(nextOffsetMetadata)声明类型是volatile,volatile类型的变量被修改时,其他所有使用到此变量的线程都能立即看到。服务端为每条消息指定绝对偏移量,会用nextOffsetMetadata的值作为起始偏移量,将消息写入日志分段中,获取到这批消息中最后一条消息的偏移量加上一后更新nextOffsetMetadata。消费者或者备份副本会根据nextOffsetMetadata最新的值拉取到新写入的消息。
为消息集分配绝对偏移量时(更新每条消息的偏移量数据:offset),以nextOffsetMetadata的偏移量作为起始偏移量。分配完后还要更新nextOffsetMetadata的偏移量值,因此获取nextOffsetMetadata的偏移量值并加一是一个原子操作。
其他补充:
日志分段
服务端处理每批追加到日志分段中的消息集,都是以nextOffsetMetadata作为起始的绝对偏移量。同一个分区的所有日志分段中,所有消息的偏移量都是递增的。
消息代理节点上的一个主题分区(TopicPartition)对应一个日志(Log)。每个日志有多个日志分段(LogSegment),一个日志管理该分区的所有日志分段。
多个日志分段中,只有一个活动日志分段(activeSegment)来存储当前接收的消息集。其中有几个重要的方法和变量:
1,activeSegment():获取segments(所有日志分段集合)中最后一个元素,作为日志最新的活动分段。如果有新的日志分段产生就会加入到segments的最后一个。
2,nextOffsetMetadata:下一个偏移量元数据,LogOffsetMetadata对象的实例,nextOffsetMetadata的构造方法中的值来源于活动日志分段:下一个偏移量的值(nextOffset),基准偏移量(baseOffset),活动日志分段的大小(size)。
3,logEndOffset:日志的最新偏移量表示下一条消息的偏移量,它的值是nextOffsetMetadata.nextOffset的值。实际上logEndOffset就是当前活动日志分段的下一个偏移量的值。
客户端对消息的读写操作会用到日志偏移量元数据(LogOffsetMetadata),写入消息集到日志,下一个偏移量元数据(nextOffsetMetadata)中的消息偏移量(nextOffset)会作为消息集的起始偏移量;从日志中读取消息时,不能超过日志结束偏移量元数据(logEndOffsetMetadata)中的结束偏移量(logEndOffset)或日志最高水位偏移量元数据(highWatermarkMetadata)的最高水位(highWatermark)。
日志偏移量元数据(LogOffsetMetadata)包含:消息偏移量(messageOffset),日志分段的基准偏移量(segmentBaseOffset),消息在日志分段中的物理位置(relativePositionInSement)。
下一个偏移量元数据(nextOffsetMetadata),日志结束偏移量元数据(logEndOffsetMetadata),日志最高水位偏移量元数据(highWatermarkMetadata)都继承自日志偏移量元数据(LogOffsetMetadata)。
nextOffsetMetadata其实和logEndOffsetMetadata的值是一样的。但是他们面向的使用对象不一样,nextOffsetMetadata是写入时使用面向生产者;logEndOffsetMetadata是读取时,但是是备份副本使用;highWatermarkMetadata读取时,但是是消费者读取使用。
追加消息集到活动日志分段,如果当前活动日志分段放不下新追加的消息集,日志会采用滚动方式创建一个新的日志分段,并将消息集追加到新的日志分段。activeSegment指向新创建的日志分段。
达到以下条件会创建新的日志分段
1,追加的消息集的大小加上原有消息集的大小超过了日志分段的阀值,也就是放不下新追加的消息集了。
2,离上次创建日志分段时间到达一定需要滚动创建的时间。
3,索引文件满了
新创建的日志分段的基准偏移量的值是nextOffsetMetadata中的消息偏移量的值,也是当前活动日志分段的下一个偏移量的值。
每个日志分段由数据文件和索引文件组成,数据文件保存消息集的具体内容,索引文件保存了消息偏移量到物理位置的索引。追加一批消息集到日志分段,数据文件实时保存消息集内容,而间隔一定的字节大小才会写入一个索引条目到索引文件中,索引文件的目的就是:快速定位指定偏移量消息在数据文件中的物理位置。基本思路是:建立消息绝对偏移量到消息在数据文件中的物理位置的映射关系。
索引文件存储的形式和特点:
1,不会为每条消息都建立索引。稀疏索引
2,索引条目的偏移量存储的是相对于基准偏移量的相对偏移量,不是消息的绝对偏移量。
3,索引条目的相对偏移量和物理位置各占4字节,即一个索引条目占用8个字节(消息集中的消息绝对偏移量占用8个字节)。
4,索引条目中的相对偏移量是有序的,查询指定偏移量时,使用二分法查找可以快速确定偏移量的位置。
5,如果指定的偏移量在索引文件中不存在,可以找小于指定偏移量的最大偏移量。
6,稀疏索引可以使用内存映射方式,将整个索引文件放入内存中,加快偏移量的查询。
总结:
1,一个日志由多个日志分段组成,日志管理了所有的日志分段。
2,日志用segments保存每个日志分段的基准偏移量到日志分段的映射关系。
3,日志分段的基准偏移量是分区级别的绝对偏移量。
4,日志分段中第一条消息的绝对偏移量等于日志分段的基准偏移量。
5,每个日志分段都由一个索引文件和一个数据文件组成。
6,日志分段的数据文件和索引文件的文件名称以基准偏移量命名。
7,数据文件保存每天消息的格式是:消息的绝对偏移量,消息的大小,消息的内容。
8,索引文件保存消息偏移量和消息在数据文件中的物理位置。
9,索引文件中索引条目的键存储的值是:消息的绝对偏移量减去基准偏移量。
10,索引文件通过内存映射方式,将整个索引文件加载到内存中,加快文件的读取。
21kafka offset 如何寻找数据 请复述流程
将分区数据存储到日志文件上时,每个分区对应一个目录,目录下有多个日志分段。同一个目录下的所有日志分段都属于同一个分区。每个日志分段在物理上由一个数据文件和一个索引文件组成。数据文件存储的是消息的真正内容,索引文件存储的是数据文件的索引信息。为数据文件建立索引文件目的是更快的访问数据文件。生产者采用追加的方式将消息写入日志文件,顺序写盘性能很高,同样消费者也是顺序读盘,但是消费者在异常恢复情况下可能需要重新处理消息,这个时候可以利用索引文件重新定位到数据文件中的消息
客户端拉取分区主副本的消息集,一定会指定拉取偏移量,发送拉取请求给服务端。服务端处理客户端的拉取请求,会返回从客户端指定偏移量开始读取的消息集,当然不能有多少消息集就返回多少消息集,客户端会指定拉取的消息量,默认大小为1M(max.partition.fetch.bytes),而日志分段对应的数据文件大小默认是1G,所以服务端在读取消息集的时候只需要选择其中一个分段,就可以满足客户端的一次拉取请求。
选择日志分段要结合客户端指定的偏移量和日志分段的基准偏移量,因为日志分段的基准偏移量是日志分段中第一条消息的偏移量,所以可以根据客户端指定的拉取偏移量从所有基准偏移量中选择出小于等于拉取偏移量的最大值的日志分段,这样的选择叫做floor。
读日志分段应该先读取索引文件再读取数据文件:根据起始偏移量(startOffset)到读取索引文件中对应的最近的物理位置(startPosition),根据起始位置直接定位到数据文件,开始读取数据文件消息,最多只能读到数据文件的结束位置(maxPosition)
由于索引文件采用稀疏索引方式存储,所有给定一个偏移量到索引文件中去查找不一定能找到对应的索引条目。但是又因为索引文件中所有索引条目的偏移量都是递增的,可以找到离目标偏移量最近的索引条目偏移量(小于目标偏移量的最大值,和找日志分段类似)
从索引文件到数据文件的步骤:
1,起始偏移量为350,找到索引文件中偏移量为345的索引条目,对应的物理位置是328.
2,根据物理位置直接定位到数据文件的328文件位置。
3,读取每条消息的偏移量,但是不读取消息内容。
4,步骤3最终会找到偏移量为350的消息,得到物理位置448。
5,定位到数据文件的448位置,开始真正的读取起始偏移量为350的消息内容。
追加消息时,索引条目的偏移量是基于日志分段基准偏移量的相对偏移盘。 由于客户端读取消息给的是绝对偏移量,因此在查询索引文件之前,要先将绝对偏移量减去日志分段的基准偏移盘,转换为相对偏移量。 另外,数据文件每条消息的偏移量存储的是绝对偏移量,查找索引文件返回值也应该是绝对偏移量 。 但索引条目存储的是相对偏移量,最后返回的偏移量还要再加上基准偏移量 。
根据给定的起始偏移量,先调用索引文件的lookup()查询方法,获得离起始偏移量最近的物理位置,然后再调用数据文件的searchFor()方法,从指定的物理位置开始读取每条消息,直到找到起始偏移量对应的物理位置。
对索引文件进行二分查找会返回两个信息:偏移量和物理位置。偏移量不一定和起始偏移量对应,物理位置也不会和起始位置对应。还需要搜索数据文件才能确定起始偏移量对应的起始位置。索引文件中每个索引条目占用8字节,索引条目中偏移量和物理位置各向占用4字节。在查找过程中,需要读取出偏移量的值,然后和目标偏移量进行比较。
文件消息集是数据文件的实现类,文件消息集的读取方法根据起始位置和读取大小,创建一个新的文件消息集视图,每次调用读取方法都会生成一个新的文件消息集对象。如果客户端每次拉取消息都在同一个日志分段中,数据文件是同一个,说明同一个文件消息集会调用多次读取方法,每次都会创建一个新的文件消息集,但所有的文件消息集都共用同一个文件和文件通道。和日志分段相关的文件消息集叫做原始文件消息集,调用原始文件消息集生成的新文件消息集叫做文件消息集视图。两种消息集有关的操作:
1,生产者生产的字节缓冲区消息集会追加到日志分段对应的原始文件消息集。
2,原始文件消息集会将自己缓冲区消息集写入到数据文件底层的文件通道中。
3,服务端处理客户端的拉取请求,读取日志分段,调用原始文件消息集的读取方法。
4,原始文件消息集读取方法会生成一个局部的文件消息集视图,和数据文件底层的文件通道相关。
5,局部文件消息集视图发送拉取响应结果给客户端,会将文件通道的字节直接传输给网络通道。
全局的可变的原始文件消息集可以接受消息集的追加,会在每次处理客户端的拉取请求时,生成不可变的局部的文件消息集视图。文件消息集视图的消息集表示一次拉取请求的分区数据,最终被封装到拉取响应中,通过服务端的网络通道发送给客户端。
22 生产者和消费者生产上如何做监控?怎样看数据是否及时消费?
jmx 监控端口
JMXtrans+InfluxDB+Grafana
metricbeat+Elasticsearch+Kibana
三种现存的:
KafkaOffsetMonitor
KafkaManager
CDH 集群监控Kafka