Kafka学习笔记

目录

1.Kafka概念

   1.1基本概念

    1.2特点

2.生产者

2.1生产者原理

2.2生产者重要参数

3.消费者

3.1消费者原理梳理

3.2消费者必要参数与重要参数

3.3消费者编码

3.4消费者的特性

3.5多线程消费者

4.kafka服务端和客户端重要特性

4.1AR和ISR

4.2分区管理

4.3消费者协调器和组协调器

4.4事务

4.5重要参数

5.面试常见题目

 

 


1.Kafka概念

   1.1基本概念

        Kafka是一个高吞吐量、分布式的发布—订阅消息系统;是一款开源的、轻量级的、分布式、可分区和具有复制备份的、基于Zookeeper协调管理的分布式流平台的功能强大的消息系统。以下是kafka中常见的概念。

1.消息:是kafka的基本单位,由一个固定长度的消息头和一个可变长度的消息体构成。

2.消费者:消息的消费方

3.生产者:消息的产生方

4.topic:消息主题,用于区分消息类型。一个topic代表一类消息

5.消费组:消费同一类消息的消费者的集合。

6.分区和副本:kafka将一组消息归纳为一个主题,每个主题被分为一个或多个分区。每个分区由一系列有序、不可变的消息组成,是一个有序队列,每个分区又有多个副本。

 

    1.2特点

        1.消息持久化,依赖于文件系统来存储和缓存信息。因此,同步kafka数据会经历经历网络→主节点内存→主节点磁盘→网络→从节点内存→从节点磁盘这几个阶段,而相对于Redis等组件的同步经历网络→主节点内存→网络→从节点内存这几个阶段,kafka需要经历磁盘,这会导致kafka同步数据的延迟更大,这也是kafka使用主写主读消费模型,而并非像redis和MySQL一样使用主写从读的消费模型的重要原因。

         2.高吞吐量。a.顺序读写 b.数据写入及数据同步采用零拷贝技术,采用SendFile()函数调用,该函数是在两个文件描述符之间直接传递数据,完全在内核中操作避免内核缓冲区与用户缓冲区之间数据的拷贝。吞吐量往往与可用性相互矛盾,这与max.in.flight.requests.per.connection配置参数相关。

         3.扩展性。使用Zookeeper来对集群进行协调管理。kafka0.8及之前版本对Zookeeper依赖太重,后期减少对Zookeeper的依赖,避免Zookeeper的性能问题影响Kafka的性能问题。

         4.多客户端支持,kafka支持多语言,只要统一协议,完全支持多语言下生产消费。

         5.数据备份。Kafka可以为每个主题指定副本数,对数据进行持久化备份。

         6.轻量级。kafka的代理是无状态的。

         7.支持流式操作。Kafka经常与流操作框架Spark等结合使用,新版本中,kafka自身的Kafka Streams支持流式操作

2.生产者

      kafka生产者是线程安全的。

2.1生产者原理

 

     

       整个过程主要由主线程和sender线程组成,主线程构造消息经过拦截器、序列化器等进入消息累加器;sender线程则从消息累加器获取消息进行发送。其中消息累加器的作用是作为消息的缓存,较少网络传输的资源消耗。以下是一些参数的调整。                 1.RecordAccumulator 缓存的大小可以通过生产者客户端参数buffer . memory 配置,默认值为33554432B ,即32M。

       2.如果生产者发送消息的速度超过发送到服务器的速度,则会导致生产者空间不足,这个时候KafkaPro ducer 的send() 方法调用要么被阻塞,要么抛出异常,这个取决于参数max.block.ms 的配置,此参数的默认值为6 0000,即60 秒。

       3.在Kafka 生产者客户端中,通过java.io.ByteBuffer 实现消息内存的创建和释放。不过频繁的创建和释放是比较耗费资源的,在RecordAccumulator 的内部还有一个BufferPool,它主要用来实现ByteBuffer 的复用,以实现缓存的高效利用。不过BufferPool 只针对特定大小的ByteBuffer 进行管理,而其他大小的ByteBuffer 不会缓存进Bu他rPool 中,这个特定的大小由batch.size 参数来指定,默认值为16384B ,即16KB 。可以适当地调大batch.size参数以便多缓存一些消息。

2.2生产者重要参数

       1.acks:这个用来指定分区中必须要有多少个副本收到这条消息,之后生产者才会认为这条消息是成功写入的。权衡消息的可靠性和吞吐量。1表示可靠性和吞吐量的折中,-1或all表示最可靠性,0表示最大吞吐量。

       2.max.request.size:表示生产者客户端能发送消息的最大值。与broker 端的message.max . bytes需要细分。

       3.retries和retry. backoff.ms:重试次数和重试间隔时间。

       4.receive.buffer.bytes:这个参数用来设置Socket 接收消息缓冲区( SO _RECBUF )的大小。

       5.send.buffer. bytes:这个参数用来设置Socket 发送消息缓冲区( SO _SNDBUF )的大小。

3.消费者

        消费者( Consumer )负责订阅Kafka 中的主题( Topic ),并且从订阅的主题上拉取消息。与其他一些消息中间件不同的是:在Kafka 的消费理念中还有一层消费组( Consumer Group)的概念,每个消费者都有一个对应的消费组。当消息发布到主题后,只会被投递给订阅它的每个消费组中的一个消费者。

3.1消费者原理梳理

        在旧消费者客户端中,消费位移是存储在ZooKeeper 中的。而在新消费者客户端中,消费位移存储在Kafka 内部的主题_consumer_offsets 中。这里把将消费位移存储起来(持久化)的动作称为“提交’3,消费者在消费完消息之后需要执行消费位移的提交。

        旧版本的消费者客户端对zookeeper有高度依赖,容易出现以下问题:

        1.羊群效应:每个消费者都对zookeeper同一路径进行操作,有可能发生死锁

        2.脑裂问题:消费者进行平衡操作时每个消费者都与zookeeper进行通信,只判断消费者或者代理变化情况,有可能出现获取不一致的情况。

 

3.2消费者必要参数与重要参数

3.2.1必要参数

1.bootstrap.servers:用来指定连接Kafka 集群所需的broker 地址清单

2.group.id:消费者隶属的消费组的名称

3.key.deserializer和value.deserializer : 与生产者客户端KafkaProducer中的key . serializer 和value . serializer 参数对应

3.2.2重要参数

1.partition.assignment.strategy:设置消费者与订阅主题之间的分区分配策略

2.enable.auto.commit:位移提交方式,默认true,消费者客户端自动提交。

3.auto.commit.interval.ms:提交位移间隔时间。默认5ms。

 

3.3消费者编码

1.客户端参数设置

Properties props= new Properties(); 
props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer”); 
props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer"); props.put("bootstrap.servers",brokerList); 
props.put("group.id",groupid); 
props.put ("client.id","XXxx");

2.创建消费者实例 ---KafkaConsumer<String , String> consumer= new KafkaConsumer<> (props );

3.订阅主题

public void subscribe(Collection<String> topics ,ConsumerRebalanceLis tener listener); public void subscribe(Collection<Str in g> topics) public void subscribe (Pattern pattern , ConsumerRebalanceListener listener);
public void subscribe (Pattern pattern);

4.拉取消息并消费

public ConsumerRecords<K, V> poll(final Duration timeout)

5.提交位移

    需要提交当前已消费位移+1:lastConsumedOffset+1。位移提交存在的问题:

  • 如果在拉取之后就提交位移,如果在消费中途出现异常,那么可能存在消息丢失的情况
  • 如果在消费之后提交位移,则可能会出现重复消费的情况。

异步提交

consumer.commitAsync(new OffsetCommitCallback() { 
      @Override 
      public void onComplete(Map<TopicPartition,OffsetAndMetadata> offsets,Exception exception ) { 
         if (exception == null) { 
              System.out.println(offsets); 
         }else { 
              log.error("fail to commit offsets {} ”, offsets , exception); 
         });

6.关闭实例

 

3.4消费者的特性

        KafkaConsumer 非线程安全并不意味着我们在消费消息的时候只能以单线程的方式执行。如果生产者发送消息的速度大于消费者处理消息的速度,那么就会有越来越多的消息得不到及时的消费,造成了一定的延迟。每个消费线程对应着都是一个TCP连接,TCP连接数目多的话会造成系统开销。所以在编码的时候,我们应该在服务启动的时候就加载消费者实例,而不应该每次消费都去创建实例,这样可以节省开销。

 

3.5多线程消费者

       构造多线程消费者有以下两种方式:

1.将每个Consumer作为一个线程。有序,但每个Consumer对应一个TCP连接,系统开销大。

2.Consumer只有一个线程,但Consumer中用线程池来对每次拉取消费都丢进线程池。无法保证顺序消费,但系统开销少,且具有横向扩展的能力,与第一个方法结合。主要维护一个共享变量,来维护每个topic对应的位移。

 

4.kafka服务端和客户端重要特性

4.1AR和ISR

       AR是指所有副本列表,ISR是指保存同步的列表,该列表中保存的是与Leader副本保持消息同步的所有副本对应的代理节点id。如果kafka设置ISR全部收到提交的消息,才算提交成功的配置,那么可以提高可靠性。因为ISR比AR中的其他副本,有着较leader副本更完整的数据。

kafka中的LEO、HW:LEO即log end offset,即下一条消息写入的位置,且分区ISR集合中的每个副本都会维护自身的LEO,所有副本中的LEO的最小值即为分区的HW(high watermark),即消费者只能消费HW之前的消息。

以下是ISR集合伸缩:

        Kafka在启动的时候会开启两个与ISR相关的定时任务,名称分别为“isr-expiration"和”isr-change-propagation".。isr-expiration任务会周期性的检测每个分区是否需要缩减其ISR集合。这个周期和“replica.lag.time.max.ms”参数有关。大小是这个参数一半。默认值为5000ms,当检测到ISR中有是失效的副本的时候,就会缩减ISR集合。如果某个分区的ISR集合发生变更, 则会将变更后的数据记录到ZooKerper对应/brokers/topics//partition//state节点中。当ISR集合发生变更的时候还会将变更后的记录缓存到isrChangeSet中,isr-change-propagation任务会周期性(固定值为2500ms)地检查isrChangeSet,如果发现isrChangeSet中有ISR 集合的变更记录,那么它会在Zookeeper的/isr_change_notification的路径下创建一个以isr_change开头的持久顺序节点(比如/isr_change_notification/isr_change_0000000000), 并将isrChangeSet中的信息保存到这个节点中。kafka控制器为/isr_change_notification添加了一个Watcher,当这个节点中有子节点发生变化的时候会触发Watcher动作,以此通知控制器更新相关的元数据信息并向它管理的broker节点发送更新元数据信息的请求。最后删除/isr_change_notification的路径下已经处理过的节点。频繁的触发Watcher会影响kafka控制器,zookeeper甚至其他的broker性能。为了避免这种情况,kafka添加了指定的条件,当检测到分区ISR集合发生变化的时候,还需要检查一下两个条件:

​      (1).上一次ISR集合发生变化距离现在已经超过5秒,

​      (2).上一次写入zookeeper的时候距离现在已经超过60秒。

​        满足以上两个条件之一者可以将ISR写入集合的变化的目标节点。

       随着follower副本不断进行消息同步,follower副本LEO也会逐渐后移,并且最终赶上leader副本,此时follower副本就有资格进入ISR集合,追赶上leader副本的判定准侧是此副本的LEO是否小于leader副本HW,这里并不是和leader副本LEO相比。ISR扩充之后同样会更新ZooKeeper中的/broker/topics//partition//state节点和isrChangeSet,之后的步骤就和ISR收缩的时的相同。

4.2分区管理

       1.分区使用多副本机制来提升可靠性,但只有leader 副本对外提供读写服务,而follower 副本只负责在内部进行消息的同步。如果一个分区的leader 副本不可用,那么就意味着整个分区变得不可用,此时就需要Kafka 从剩余的fo llower 副本中挑选一个新的leader 副本来继续对外提供服务。

       2.在创建主题的时候,该主题的分区及副本会尽可能均匀地分布到Kafka 集群的各个broker节点上,对应的leader 副本的分配也比较均匀。

       3.针对同一个分区而言, 同一个broker 节点中不可能出现它的多个副本, 即Kafka 集群的一个broker 中最多只能有它的一个副本, 我们可以将leader 副本所在的broker 节点叫作分区的leader 节点,而follower副本所在的broker 节点叫作分区的follower 节点。

       4.分区是Kafka 中最小的并行操作单元,对生产者而言,每一个分区的数据写入是完全可以并行化的: 对消费者而言, Kafka 只允许单个分区中的消息被一个消费者线程消费, 一个消费组的消费并行度完全依赖于所消费的分区数。

4.3消费者协调器和组协调器

        1.每个消费组的子集在服务端对应一个GroupCoordinator 对其进行管理, GroupCoordinator 是Kafka 服务端中用于管理消费组的组件。而消费者客户端中的ConsumerCoordinator 组件负责与GroupCoordinator 进行交互。

        2.GroupCoordinator 需要为消费组内的消费者选举出一个消费组的leader ,这个选举的算法也很简单,分两种情况分析。如果消费组内还没有leader,那么第一个加入消费组的消费者即为消费组的leader。如果某一时刻leader 消费者由于某些原因退出了消费组,那么会重新选举一个新的leader,这个重新选举leader 的过程随机选取。

       3.leader 消费者根据在第二阶段中选举出来的分区分配策略来实施具体的分区分配,在此之后需要将分配的方案同步给各个消费者,此时leader 消费者并不是直接和其余的普通消费者同步分配方案,而是通过GroupCoordinator 这个“中间人”来负责转发同步分配方案的。在第三阶段,也就是同步阶段, 各个消费者会向GroupCoordinator 发送SyncGroupRequest 请求来同步分配方案.只有leader消费者发送的SyncGro upR equest 请求中才包含具体的分区分配方案,这个分配方案保存在group_assIgnment 中,而其余消费者发送的SyncGroupRequest 请求中的group_assignment为空。group_assignment 是一个数组类型,其中包含了各个消费者对应的具体分配方案.

       4.消费者的心跳间隔时间由参数heartbeat. interval.ms指定,默认值为3000 ,即3 秒, 这个参数必须比session . timeout.ms 参数设定的值要小,一般情况下heartbeat.interval.ms 的配置值不能超过session.timeout.ms(GroupCoordinator等待消费者的时间,过了这个时间就认定消费者死亡,GroupCoordinator进行再均衡)配置值的1/3 。这个参数可以调整得更低,以控制正常重新平衡的预期时间。

      5.还有一个参数max.poll.interval.ms ,它用来指定使用消费者组管理时poll()方法调用之间的最大延迟,也就是消费者在获取更多消息之前可以空闲的时间量的上限。如果此超时时间期满之前poll()没有调用, 则消费者被视为失败,并且分组将重新平衡, 以便将分区重新分配给别的成员。

4.4事务

     1.一般而言,消息中间件的消息传输保障有3 个层级,分别如下。

     (1) at most once :至多一次。消息可能会丢失,但绝对不会重复传输。

     (2) at least once : 最少一次。消息绝不会丢失,但可能会重复传输。

     (3) exactly once :恰好一次。每条消息肯定会被传输一次且仅传输一次。

     2. Kafka消费者根据拉取消息后,提交位移以及处理消息的顺序,可以分别实现at least once和at most once。Kafka 从0.11.0.0 版本开始引入了军等和事务这两个特性,以此来实现EOS ( exactly oncesemantics ,精确一次处理语义)。

     3.开启幕等性功能的方式很简单,只需要显式地将生产者客户端参数enable.idempotence设置为true 即可(这个参数的默认值为false ).不过如果要确保军等性功能正常,还需要确保生产者客户端的retries 、acks 、max .in. flight.reques t s . per . connection 这几个参数不被配置错。实际上在使用幕等性功能的时候,用户完全可以不用配置(也不建议配置)这几个参数。

     4.每个新的生产者实例在初始化的时候都会被分配一个P ID , 这个PID 对用户而言是完全透明的。对于每个PID,消息发送到的每一个分区都有对应的序列号,这些序列号从0 开始单调递增。生产者每发送一条消息就会将<PID , 分区>对应的序列号的值加1.

     5.幂等性并不能跨多个分区运作,而事务可以弥补这个缺陷。为了实现事务,应用程序必须提供唯一的transactionalld ,这个transactionalld 通过客户端参数transactional.id 来显式设置.transactionalld 与PID 一一对应,两者之间所不同的是transactionalld 由用户显式设置,而PID是由Kafka内部分配的。

 

 

4.5重要参数

1.retries:重试次数

2.retry.backoff.ms:两次重试之间的间隔

3.unclean.leader.election.enable=false   关闭unclean leader选举,即不允许非ISR中的副本被选举为leader,以避免数据丢失

4.min.insync.replicas 消息至少要被写入到这么多副本才算成功,也是提升数据持久性的一个参数。与acks配合使用

5.max.in.flight.requests.per.connection = 1 限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序,但会影响吞吐

6.replication.factor:副本因子。

 

 

 

 

 

5.面试常见题目

1.在kafka新版本中为何减少消费者对zookeeper的依赖?

        因为老版本消费者对于zookeeper的依赖比较重,对于zookeeper的I/O比较频繁,容易引起羊群效应和脑裂效应。

2.为何kafka使用主写主读模型,而不像redis和masql一样使用主读从写模型?

        1.数据一致性问题

        2.延迟问题:因为kafka会将数据存到本地硬盘,所以同步数据需要从硬盘读取,所以延迟会比redis和mysql长。

        3.因为kafka引入了分区与副本,所以对于多broker的kafka集群来说,只要分区副本分配的算法合理,负载就会比较均衡,这是redis所没有的特性。

3.如何理解kafka的主题、分区以及副本?以及消费者、消费者组和分区、主题之间的关系?

       消费者组订阅这个主题,意味着主题下的所有分区都会被组中的消费者消费到,如果按照从属关系来说的话就是,主题下的每个分区只从属于组中的一个消费者,不可能出现组中的两个消费者负责同一个分区,因为这样会出现同一个分区位移被覆盖的情况,导致数据丢失或者重复消费,比如A,B消费者同时消费一个分区,A的lastoffset=5,B的lastoffset=20,如果B先提交,B再提交则会覆盖位移,那么会造成重复消费,反之则会造成数据丢失。分区是从属于topic的概念,分区又是位于broker的分区,副本是针对分区的副本。

4.kafka如何选举leader副本?

      当leader副本宕机之后,会按照AR 集合中副本的顺序查找第一个存活的副本,并且这个副本在ISR 集合中。

5.说说对kafka控制器的理解?

       1.控制器选举:控制器选举依赖于Zookeeper,成功竞选为控制器的broker 会在ZooKeeper中创建/controller 这个临时( EPHEMERAL )节点。每个broker启动时都会去读取/controller节点下面的brokerId,如果brokerId为-1则尝试创建/controller节点,利用zookeeper分布式锁的特性。ZooKeeper 中还有一个与控制器有关的/controller_epoch 节点,这个节点是持久(PERSISTENT )节点,节点中存放的是一个整型的controller epoch 值。controller_epoch 用于记录控制器发生变更的次数,即记录当前的控制器是第几代控制器,我们也可以称之为“控制器的纪元”。

       2.在目前的新版本的设计中,只有Kafka Controller在ZooKeeper 上注册相应的监昕器,其他的broker 极少需要再监听ZooKeeper 中的数据变化,此来监昕此节点的数据变化( ControllerCbangeHandler )。

       3.目前版本通过控制器来与zookeeper进行交互,避免了之前对于zookeeper过度依赖造成的性能问题。

 

6.新版本如何解决数据同步时数据丢失和数据不一致的问题?

      引入leader epoch解决数据同步不一致的情形。新版本kafka(0.11之后),A 不是先忙着截断日志而是先发送OffsetsForLeaderEpochRequest请求给B,B会将leader epoch返回给A,这样A就知道哪些数据需要删除,然后向B发送同步请求,B返回响应给A,A完成同步。引入leader epoch相当于引入了数据版本,以及每个数据版本对应的起始位移,这样可以判断follower副本与leader副本的区别以及判断出哪些是有效数据,并进行相应的同步,解决了原有的不一致性问题。

 

7.说说kafka中的几个选举leader过程?

       1.控制器选择:成功竞选为控制器的broker 会在ZooKeeper中创建/controller 这个临时( EPHEMERAL )节点,利用zookeeper临时节点的分布式锁特性进行选举。

       2.消费者leader选举:GroupCoordinator 需要为消费组内的消费者选举出一个消费组的leader ,这个选举的算法也很简单,分两种情况分析。如果消费组内还没有leader,那么第一个加入消费组的消费者即为消费组的leader。如果某一时刻leader 消费者由于某些原因退出了消费组,那么会重新选举一个新的leader,这个重新选举leader 的过程随机选取。

       3.副本leader选举:按照AR 集合中副本的顺序查找第一个存活的副本,并且这个副本在ISR 集合中。一个分区的AR 集合在分配的时候就被指定,并且只要不发生重分配的情况,集合内部副本的顺序是保持不变的,而分区的ISR 集合中副本的顺序可能会改变

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值