Kafka总体架构
- producer:生产者
- consumer:消费者
- Consumer group: 消费组,由多个consumer组成,消费者组 每个消费者负责消费不同分区的数据,一个分区只能由一个消费者组内消费者消费,消费者组之间互不影响,
- broker:一台kafka服务器就是一个broker,一个集群由多个broker组成,一个broker可以容纳多个topic
- topic:主题,理解为 一个队列生产者和消费者面向的都是一个topic
- partition:为了实现扩展性,一个非常大的topic可以分不到多个broker上面,一个topic可以分成多个partition,每个partition都是一个有序的队列
- replica:一个topic的每个分区由多个副本,一个leader和若干个follower
- leader:每个分区多个副本的主要的
- follower:每个分区多个副本的从
kafka生产者
消息发送的流程里面,kafka设置了两个线程一个main线程一个sender线程,在main线程里面创建了一个双端队列 recordaccumulator, main线程先将消息发送到recordAccumulator,sender线程不断从recordaccumulator中拉去消息发送到kafka的broker。
-
bath.size:只有数据积累到batch.size之后sender才会发送数据,默认16k
-
linger.ms: 如果数据迟迟味道达batch.size,sender线程就会等待lingerms才会发送数据
-
应答 ack 的选择:
- 0:生产者发送xia数,不需要等待数据落盘
- 1:生产者发送数据过来 leader 收到完整数据后应答
- -1:生产者发送的数据过来只有等到 leader 和 isr 里面存活的节点都落盘才应答
对于数据的传递语义
- 至少一次(At Least Once) = ACK级别设置为-1 + 分区副本大于等于2 + ISR里应答的最小副本数量大于等于2,保证消息不丢失,但是可能数据重复
- 最多一次(At Most Once) = ACK级别设置为0,保证消息不重复,但是可能丢失
- 精确一次(Exactly Once)= 幂等性 + 至少一次( ack =-1 + 分区副本数>=2 + ISR 最小副本数量>=2)数据既不能重复也不丢失,幂等性和事务。
幂等性的保证:具有相同主键的消息提交时,Broker只会持久化一条。其 中PID是Kafka每次重启都会分配一个新的;Partition 表示分区号;Sequence Number是单调自增的。所以幂等性只能保证的是在单分区单会话内不重复
其中ProducerRecord
是我们使用KafkaProducer发送消息时拼装的单条消息,而ProducerBatch
可以看做是针对一批消息进行的封装,因为会在RecordAccumulator中执行**tryAppend
** 方法将一批消息拼装在一起,可以减少网络请求次数从而提升 吞吐量 。
Kafka通过ByteBuffer来实现字节形式的网络传输,为了减少频繁创建/释放ByteBuffer所造成的资源消耗,Kafka还提供了缓冲池(BufferPool)来实现ByteBuffer的回收,再其内部维护了Deque<ByteBuffer> free
变量来保存空闲ByteBuffer,还提供了Deque<Condition> waiters
变量来保存阻塞等待中的线程。
如果待分配的size等于缓冲池中ByteBuffer的大小(可由batch.size
参数进行配置,默认为16Kb),则直接从free队列中拿出空余的ByteBuffer供其使用;否则,判断如果缓冲池中空闲ByteBuffer的内存总和加上非缓冲池内存大小是大于待分配size的,则采用非缓冲池
加上缓冲池
混合释放内存的方式进行内存分配
数据有序
数据有序两个方向实现:
- 单分区内有序
- 多分区,在消费端缓存一个队列将消息缓存之后进行排序
数据乱序
Sender线程则异步从消息累加器中获取缓存的消息,然后将其转为指定格式的ProducerRequest对象,将Request对象请求发往各个broker了。不过,请求在从Sender线程发往broker之前还会被保存到InFlightRequests中,其主要作用是缓存了已经发出去但还没有收到服务端响应的请求。同时在kafka集群也会缓存5个request的元数据,用以保证数据有序
生产者发送消息的分区策略
数据有序
- 单分区数据有序
- 多分区可以在消费端缓存接受的消息然后进行排序
Kafka的分区副本与节点选取
副本的作用:提高数据的可靠性,默认一个副本,生产环境一般配置两个
kafka中的分区中所有副本统称AR(ASSIGNED REPILICAS)
AR = ISR + OSR
ISR表示和leader节点同步的follower集合,如果follower长时间未像leader发送信息就会提出isr
OSR,表示 Follower 与 Leader 副本同步时,延迟过多的副本
文件存储
在kafka的文件存储机制里面,topic是逻辑上的概念,partition是物理上面的概念,每个partition对应一个log文件,该log文件就是producer生产的数据,一般以追加的方式加到log文件末端,kafka为了方式log文件过大,导致定位效率低下采用了分片加稀疏索引的方式将每个partition分为多个segement,每个segement包括了一个index文件一个log文件和一个timeindex文件,这些文件在一个文件夹下面文件夹的命名规则未:topic+分区序号
log : 日志文件
index:偏移量的索引文件
timeindex:时间戳索引文件
对于kafka中的日志默认是保留7天,超过时间一般就会有两种策略来进行:
- delete 日志删除:将过期数据删除
- compact日志压缩:对于相同key的不同value值,只保留最后一个版本。
kafka高效读取数据
- kafka本身是分布式集群,可以采用分区技术,并行度高
- 存数据的时候一方面使用了页缓存技术+零拷贝技术,一方面顺序追加写的方式
- 读取数据的时候是采用的稀疏索引
分区的分配以及再平衡
- Range:相对于一个topic而言,partitions数/consumer数 来决定每个消费者应该 消费几个分区。如果除不尽,那么前面几个消费者将会多 消费 1 个分区(积累很多会造成****数据倾斜 )
- RoundRobin: 轮询分区策略,是把所有的 partition 和所有的 consumer 都列出来,然后按照 hashcode 进行排序,最后 通过轮询算法来分配 partition 给到各个消费者 。
- Sticky: 会尽量均衡的放置分区 到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分 区不变化
- CooperativeSticky:
Offset
虽然自动提交offset十分简单便利,但由于其是基于时间提交的,开发人员难以把握offset提交的时机。因 此Kafka还提供了手动提交offset的API。 手动提交offset的方法有两种:分别是commitSync(同步提交)和commitAsync(异步提交)。两者的相 同点是,都会将本次提交的一批数据最高的偏移量提交;不同点是,同步提交阻塞当前线程,一直到提交成 功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故 有可能提交失败
- commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。
- commitAsync(异步提交):发送完提交offset请求后,就开始消费下一批数据了
漏消费和重复消费
重复消费:已经消费了数据,但是offset没有提交
漏消费:先提交 offset 后消费,有可能会造成数据的漏消费。
如果想完成Consumer端的精准一次性消费,那么需要Kafka 消费端将消费过程和提交offset 过程做原子绑定。此时我们需要将Kafka的offset保存到支持事务的自定义介质(比 如 MySQL)
数据积压
- 如果是kafka消费能力不足,可以考虑添加topic的分区,并且同时提升消费组的消费者数量,消费者数=分区数
- 如果是下游的数据处理不及时:提高每批次的拉取数量,批次数量拉去过少会导致处理的数据小于生产的数据,也会造成数据挤压
未完待续。。。
参考《尚硅谷kafka》学习