一文让你了解的Kafka重点原理

Kafka原理解析


文章目录


Java、大数据开发学习要点(持续更新中…)


一、什么是消息队列?什么是Kafka?

1.1 消息队列

  消息队列就是用于数据生产方和消费方解耦合的中间件。顾名思义,主体就是一个队列的形式收集消息,数据在消费端按照FIFO的原则被消费。

1.2 Kafka概念与基础架构

					Kafka是一个基于发布订阅模式的分布式消息队列

Kafka基础架构如下图所示:
一文让你了解的Kafka重点原理

  1. 为了方便扩展和提高吞吐量,每个Topic分为多个Partition
  2. 配合Topic分区的设计,提出消费者组的概念,每个组内的消费者并行消费一个Topic中的不同Partition中的数据。(但是整体上还是一个Topic为一个队列,并且消费者数与分区数可以不同)
  3. 为了高可用性,为每个Partition在不同broker节点存放了副本。(仅仅是Follower不接受请求)

二、Kafka架构深入!!

2.1 Kafka存储模型

  Kafka中消息是以Topic进行分类的,生产者生产消息,消费者消费消息,都是面向Topic的。而Topic在物理上的存储是分区存储的,即按Partition分布式存储。每个Partition中的数据又是顺序写入log文件1中进行存储。
  但这样还是会出现分区log文件过大,导致的读取性能下降的问题。所以Kafka将log文件切分成了segment每个segment由 .log数据存储文件 和 .index索引文件 和 .timeindex文件组成。详细的结构如下图所示:
一文让你了解的Kafka重点原理

而每个log文件和index文件的命名就是 文件中起始数据的偏移量,一个segment中由index定位到对应log文件中执行数据的原理如下图:
一文让你了解的Kafka重点原理
index文件中根据需要查找的offset根据保存起始偏移量(文件名)的相对偏移量,定位到log中数据真实的位置。

  • 1注解1:类似HBase中的顺序写入HFile,磁头寻址次数少,顺序读/写性能好。

2.2 Kafka Producer

2.2.1 数据分区

如前面所提到的,Kafka是分布式的消息队列,分区的目的是:

							总结就是:充分利用分布式的优势
  • 分区方便后期拓展
  • 分区能够增大读写的吞吐量。

分区原则:指定了Partition的直接写入对应Partition、否则根据key进行hash后对分区数取余、没有key的通过random-robin算法得到分区(第一个得到一个随机数,后续的在此基础上自增)

2.2.2 数据可靠性保证

  为保证Producer发送的数据,能可靠的发送到指定的Topic,Topic的每个Partition收到Producer发送的数据后,都需要向Producer发送ack响应,如果Producer收到ack,就会进行下一轮的发送(不是同步的,而是异步的,分批次检查前面发送的消息ack是否收到),否则重新发送数据。

  • ack应答机制:Kafka设定了三种类型的应答机制

acks = 0:Producer不用等待broker的ack,但是broker未将数据写入前宕机会产生数据丢失
acks = 1:Producer等待broker的ack,Partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会数据丢失
acks = -1(常用):Producer等待broker的ack,Partition的leader和follower全部落盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复

  • ISR:ISR是用于解决Partition的leader等待所有follower同步,然而某些follower由于某些原因迟迟不能完成同步的问题的follower动态集合。那些无法完成同步的follower会被踢出集合;在其恢复后,同步了follower数据后再重新加入集合。
  • 故障恢复
  1. follower故障后会被踢出ISR,在其恢复后读取HW(high watermark,ISR中所有副本中结尾offset的最小值),截断log后重新向leader同步HW后的数据。完成后重新加入ISR。

  2. leader故障后,ZK在ISR中选出新的leader,为保证副本数据一致性,follower会将自身HW后的数据截断,重新向新的leader同步

     *注意:上述只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。
    

2.2.3 Exactly-Once语义

  在保证数据不丢失的场景下,一般将Kafka的ack应答设置为-1,那么也就存在数据重复的可能性(At-Least-Once)。而要实现数据的Exactly-Once(每条数据在Kafka中有且只有一条不会重复也不会丢失),需要额外实现幂等性,也即:

						At-Least-Once + 幂等性 = Exactly-Once

为了实现幂等性,Producer端会分配一个PID,发往同一个Partition的消息会附带Sequence Number(offset);而Broker端会对<PID,Partition,SeqNum>进行缓存,当收到相同主键的消息将不再存储。

幂等性在跨分区跨会话时会失效(当Producer挂了重启后,主键PID和Partition会发生变化) ——需要通过事务解决

2.2.4 Producer数据提交流程

一文让你了解的Kafka重点原理Kafka的Producer发送消息采用的是异步发送的方式。在消息发送的过程中,涉及到了两个线程——main线程和Sender线程,以及一个线程共享变量——RecordAccumulator。main线程将消息发送给RecordAccumulator,Sender线程不断从RecordAccumulator中拉取消息发送到Kafka broker。

注意:
1.消息在Producer端就已经分区了。
2.消息是按照batch size发送的,但当数据未达到batch size超时了也会被强制发送。

2.3 Kafka Consumer

2.3.1 消费模式

  Kafka的消费模式是poll模式,就是每个消费者按照自己的消费能力在Broker中读取数据。但有一个问题就是,如果没有新的数据,那么消费者就在循环中空转,这个问题Kafka设置了一个短暂的timeout来让消费者在没有数据可以消费的时候等待一小会。

2.3.2 分区分配策略

  如果Consumer组中Consumer数量>Topic Partition数量根据Random-Robin策略(实际是个轮询)或者Range(划分范围,一个范围内的给到一个消费者)对Partition进行分配。此后这个消费者组中的消费者消费的Partition就被定下来了。

2.3.3 offset维护

  comsumer需要记录已经消费到的偏移量以便故障或者后续继续消费。在0.9版本后Kafka将这些信息都保存在一个内置的Topic中(__comsumer_offset),而此前的版本是保存在ZK中的(1.优化效率,减轻ZK压力 2.可以自己实现偏移量维护)

2.4 Kafka高效读写的保证~

  1. 如同HBase顺序写HFile文件一样,Kafka顺序写log文件写入磁盘效率极高(据Kafka官网文档说比随机写快6000倍)。
  2. 使用操作系统的Page Cache来缓存要写入的数据,好处在于:
    (1)写入前可以做一些优化,提高磁盘写入性能
    (2)缓存也可以用于数据被读取,当数据写入与读取速率相近的情况下,可以直接内存读取。
    (3)Page Cache非JVM内存,不会影响JVM,导致GC的增加。同时,Kafka节点宕机,数据还在此机器缓存。
  3. 零拷贝机制
    很多应用其实在文件从磁盘拷贝到磁盘、从磁盘拷贝到Socket缓存,这些应用不需要接手这些数据。而一般的拷贝机制要经历从例如磁盘 -> 内核page cache -> 应用缓存 -> 内核page cache -> 磁盘的过程,如果数据只是单纯的拷贝而不需要修改,那么拷贝到应用缓存的步骤完全是多余的。所以Kafka利用了操作系统提供的零拷贝机制,来减少不需要的系统调用和数据拷贝次数

2.5 Kafka如何通过ZK来进行选举和状态更新?

  首先,Kafka集群启动时,会从Broker中选举一个Controller(分布式锁实现抢先创建临时节点的broker当选),负责管理集群Broker的上下线(监听zk的/brokers/ids/节点)所有Topic分区副本分配leader选举等工作。

当某个Broker挂了以后,Controller监听到临时节点/brokers/ids/中的变化,从ZK各个分区状态信息中获取ISR(此时去除了挂掉节点所有的Partition,失去leader的Partition重新选举leader),并完成ZK各个分区状态更新

2.6 Kafka事务!

  Kafka事务在0.11版本后引入,主要解决的是 Producer在Exactly Once语义上跨分区跨会话的精准一次写入,要么成功要么失败。

  • Producer事务(断点续传)
      为了实现跨分区跨会话的事务,每个Producer需要一个全局唯一的Transaction ID,并将Producer获得的PID和Transaction ID绑定。这样当Producer重启后就可以通过正在进行的Transaction ID获得原来的PID。
      为了管理Transaction,Kafka引入了一个新的组件Transaction Coordinator(事务调度器)。Producer就是通过和Transaction Coordinator交互获得绑定的PID和对应的任务状态。Transaction Coordinator还负责将事务所有写入Kafka的一个内部Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。

*注意:Kakfa事务回滚不会直接去删除消息,而是将消息对Consumer不可见。

  • Consumer事务
    Kafka对Consumer的事务较弱,一般是通过Consumer端自己实现精确一次性消费(将消费过程和提交offset作为一个原子操作实现)。

2.7 手动维护offset

			实现精准一次性消费,手动维护offset是基础,但也无法避免重复消费

  Kafka消费偏移量的维护默认由Kafka通过维护Topic(__comsumer_offset)来实现,默认5s自动提交一次。但这样做Kafka在记录偏移量前宕机,则消费者会重复消费
  单纯的手动提交偏移量如果消费行为和手动提交行为不是一个原子行为,那么消费者在消费完未提交偏移量期间宕机,数据会发生重复消费的现象。解决方法有两种:

  1. 在消费者端去重,在每次宕机重启后对新消费数据去重。
  2. 将消费和偏移量提交绑定为原子化操作(事务),消费端将offset维护到外部介质中。
上一篇:KafkaProducer源码


下一篇:java连接kafka测试