Kafka大体

100 阅读7分钟

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高效读取数据

  1. kafka本身是分布式集群,可以采用分区技术,并行度高
  2. 存数据的时候一方面使用了页缓存技术+零拷贝技术,一方面顺序追加写的方式
  3. 读取数据的时候是采用的稀疏索引

分区的分配以及再平衡

  • 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)

数据积压

  1. 如果是kafka消费能力不足,可以考虑添加topic的分区,并且同时提升消费组的消费者数量,消费者数=分区数
  2. 如果是下游的数据处理不及时:提高每批次的拉取数量,批次数量拉去过少会导致处理的数据小于生产的数据,也会造成数据挤压

未完待续。。。

参考《尚硅谷kafka》学习