Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。
消息的发布者不会将消息发给特定的订阅者,而是将发布的消息分为不同的类别,订阅者只接受感兴趣的消息。
1)Kafka 本身是分布式集群,可以采用分区技术,并行度高
2)读数据采用稀疏索引,可以快速定位要消费的数据
3)顺序写磁盘,写的过程是一直追加到文件末端,省去了大量磁头寻址的时间。
4)页缓存 + 零拷贝技术
topic是逻辑概念,partition是物理层面的概念,每个partition对应一个log文件夹,producer生产数据会不断追加到log文件末端,防止log文件过大导致定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment。每个segment包括:“.index”文件、“.log”文件和.timeindex等文件。
实用工具查看log文件以及索引文件
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.index
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.log
步骤
①根据目标offset定位到Segment文件
②找到小于等于目标offset的最大offset对应索引项
③定位到log文件
④向下遍历找到record
Kafka 中默认的日志保存时间为 7 天,可以通过调整如下参数修改保存时间。
日志超过设置时间,日志清理策略
如果一个 segment 中有一部分数据过期,一部分没有过期,怎么处理?
处理的规则:等待获取最新数据时间戳,也就是所有记录中的最大时间戳,作为判断过期的依据,然后进行清理。
副本它的作用是提高数据可靠性,kafka默认副本数是一个,在生产环境中一般配置俩个,保证数据的可靠性,如果太多的副本会增加磁盘存储空间,增加网络上数据传输,降低效率。
Kafka 分区中的所有副本统称为 AR,AR又分为ISR和OSR
ISR(in -sync Replicas):
①达到时间阈值和消息条数阈值,如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。
②Leader 发生故障之后,就会从 ISR 中选举新的 Leader。
OSR(out -sync Replicas):
表示 Follower 与 Leader 副本同步时,延迟过多的副本。
Leader 发生故障后,就会从 ISR 中选举出新的 Leader。
当follower变成主节点后,其他follower开始从新的leader同步offset。如果新leader的LEO低于follower,那么follower多余的数据会被删除;最终保证和主节点的数据—致。
kafka leader选举流程
(1)查看 Leader 分布情况
kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic hello
bin/kafka-server-stop.sh
(3)查看leader分区情况:broker=1被提出ISR
broker=1的节点不可用
(4)启动节点broker=1的节点 leader分区情况
生产
一个consumer group中有多个consumer组成,一个 topic有多个partition组成,现在的问题是,到底由哪个consumer来消费哪个partition的数据。
可以通过配置参数partition.assignment.strategy,修改分区的分配策略。
Range 是对每个 topic 而言的。
首先对同一个 topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。
通过 partitions数/consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。
如果一个消费者挂掉了如何实现再平衡呢
一个消费者挂掉后,会把他的任务会整体被分配到其他消费者,消费者组需要按照超时时间45s来判断它是否退出,所以需要等待,时间到了45s后,判断它真的退出就会把任务分配给其他broker执行。
RoundRobin 针对集群中所有Topic而言。
RoundRobin 轮询分区策略,是把所有的 partition 和所有的consumer 都列出来,然后按照 hashcode 进行排序,最后通过轮询算法来分配 partition 给到各个消费者。
一个消费者挂掉,会按轮询的方式把它的数据分给其他的消费者,也是再过45s后确认是否真正退出。
首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化
一个消费者挂掉之后,会按照粘性规则,尽可能随机分配分区,有其他消费者消费。
生产者发送消息
将数据发往指定 partition 的情况下,例如,将所有数据发往分区 1 中。
生产者端
没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值
key 值为 a,b,f
生产者把数据存放在leader分区,存放完毕后,消费者组开始消费分区数据,消费过程中会有offset,就是指消费者消费到哪里
0.9之前,维护在zookeeper中,但是会造成一个问题,每个消费者都会和zookeeper进行通讯,造成大量网络通讯问题,效率比较低下
0.9之后。默认将offset存放在kafka一个内置的topic中,名称为_consumer_offsets
__consumer_offsets 主题里面采用 key 和 value 的方式存储数据。key 是 group.id+topic+分区号,value 就是当前 offset 的值。每隔一段时间,kafka 内部会对这个 topic进行compact,也就是每个 group.id+topic+分区号就保留最新数据。
enable.auto.commit:是否开启自动提交offset功能,默认是true
auto.commit.interval.ms:自动提交offset的时间间隔,默认是5s
产生的问题: 重复消费
1) Consumer:每5s提交offset
2)如果提交offset后的2s,consumer挂了
3)再次重启consumer,则从上一次提交的offset处继续消费,导致重复消费
手动提交offset的方法有两种:分别是commitSync(同步提交)和commitAsync(异步提交)。
commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。
commitAsync(异步提交) :发送完提交offset请求后,就开始消费下一批数据了。
相同点:都会将本次提交的一批数据最高的偏移量提交
不同点:同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故有可能提交失败。
产生的问题: 漏消费
当offset被提交时,数据还在内存中未落盘,此时刚好消费者线程被kill掉,那么offset已经提交,但是数据未处理,导致这部分内存中的数据丢失。
幂等性就是指Producer不论向Broker发送多少次重复数据,Broker端都只会持久化一条,保证了不重复。
幂等性的实现原理,kafka引入了PID和Sequence Number。
PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
Sequence Numbler。(对于每个PID,该Producer发送数据的每个
幂等性无法实现跨分区和跨会话数据不重复,因为服务器在重启之后,首先PID,就是刚才所说的生产者分配的ID会被打乱,所以无法实现跨会话,然后就是partition的主键会被打乱,所以幂等性无法实现跨分区的数据不重复
弥补了这一缺陷,需要开始事务
事务它的实现原理: Producer 端引入了一个 TransactionalId 来解决这个问题,它可以用来标识一个事务操作,便于这个事务的所有操作都能在一个地方(同一个 TransactionCoordinator)进行处理,这个操作把消费和生产的处理在一 个原子单元内完成,操作要么全部完成、要么全部失败。