kafka

kafka 重点概念

什么是kafka

Kafka已经定位为一个分布式流式处理平台,它以高吞吐、可持久化、可水平扩展、支持流数据处理等多种特性而被广泛使用。

  • 消息系统
    Kafka和传统的消息系统(也称作消息中间件〉都具备系统解稿、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等功能。与此同时,Kafka还提供了大多数消息系统难以实现的消息顺序性保障及回溯消费的功能。
  • 存储系统
    Kafka把消息持久化到磁盘,相比于其他基于内存存储的系统而言,有效地降低了数据丢失的风险。也正是得益于Kafka的消息持久化功能和多副本机制,我们可以把Kafka作为长期的数据存储系统来使用,只需要把对应的数据保留策略设置为“永久”或启用主题的日志压缩功能即可。
  • 流式处理平台
    Kafka不仅为每个流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理类库,比如窗口、连接、变换和聚合等各类操作。

消息队列的两种模式

  • 点对点模式
    • 模式特点:消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。消息被消费以后,queue中不再存储该条消息,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者消费。
    • 数据拉取方式:消费者主动拉取。
    • 模式缺点:消息不能被重复消费。

img

  • 发布订阅模式

    • 模式特点:消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。
    • 数据拉取方式:消费者主动拉取、消费者被动接受
    • 模式缺点:当数据拉取方式为消费者被动接受时,消费者的消费速度可能跟不上生产者的生产速度。

    在这里插入图片描述

架构图

Kafka体系架构包括若干Producer、若干Broker、若干Consumer,以及一个ZooKeeper集群。其中ZooKeeper是Kafka用来负责集群元数据的管理、控制器的选举等操作的。

Producer将消息发送到Broker,Broker负责将收到的消息存储到磁盘中,而Consumer负责从Broker订阅并消费消息。

在这里插入图片描述

producer(生产者)

生产者发送到broker某个topic的所有数据都有其特定的partition+offset,这样可以且已确定一条消息。通过不断顺序追加到文件末尾完成消息的相对有序存储。在单个partition内部数据的存储结构为顺序存储,每一条消息都有一个唯一的且单调递增的offset值,保证单个partition的顺序性。

描述

consumer(消费者)

消费者每消费一条数据,offset加1。可以是单个消费者进行消费数据,也可以是多个消费者共同消费数据。 不同消费者同一group消费数据时,消费者之间是竞争关系(互相影响,一个拿了A,另一个就拿不到A);不同消费者不同group消费数据时,消费者之间是平行关系(互不影响,都拥有数据A);

描述

consumer group (消费者组)

消费者组,由多个消费者组成,消费者组内每个消费者负责消费不同分区的数据,一个分区只能被一个消费者消费,消费者组之间互不影响。所有的消费者都属于某个消费者组。

broker (节点存储数据)

一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。

保证高可用性,每个broker中都有4个partition ,但是每个partition的leader节点分别在4个broker中。正常与外部交互时候只有leader节点,其他partition(follower)由leader节点同步数据作为备份,保证宕机切换数据一致性、高可用性。

描述

topic (主题)

生产者和消费者面向的都是一个topic;

partition (分区)

为了实现扩展,一个topic可以分布在多个节点(broker)上,一个topic可以分为多个partition,每个partition都是一个有序的队列;kafka中分区内有序,分区和分区之间不能保证全局有序性;

img

Replica(副本)

为保证集群中的某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能够继续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个follower。

img

leader

每个分区的主副本,生产者发送数据的对象,以及消费者消费数据的对象都是leader

Follower

每个分区多个副本的从副本,实时从leader中同步数据,保持和leader数据的同步,leader发生故障时,某个follower会成为新的leader

zookeeper

存放元数据,成员管理,controller选举,

如: leader 检测、分布式同步、配置管理、识别新节点何时离开或连接、集群、节点实时状态等等。

  • 存放元数据:主题分区的所有数据都保存在zk中
  • 成员管理:broker节点的注册
  • controller选举:选举集群controller,而其他管理类任务

生产者

生产者架构(死记在脑海中!!!)

在这里插入图片描述

整个生产者客户端由两个线程协调运行,这两个线程分别为主线程和Sender线程(发送线程)。在主线程中由KafkaProducer创建消息,然后通过可能的拦截器、序列化器和分区器的作用之后缓存到消息累加器(RecordAccumulator,也称为消息收集器〉中。Sender线程负责从RecordAccumulator中获取消息并将其发送到Kafka中。

RecordAccumulator主要用来缓存消息以便Sender线程可以批量发送,减少网络传输的资源消耗以提升性能。RecordAccumulator缓存的默认大小为32MB,如果生产者发送消息的速度超过发送到服务器的速度,则会导致生产者空间不足,send()方法调用要么被阻塞,要么抛出异常,这个取决于参数max.block.ms的配置,此参数的默认值为60000,即60秒。

主线程中发送过来的消息都会被迫加到RecordAccumulator的某个双端队列(Deque)中,在RecordAccumulator的内部为每个分区都维护了一个双端队列,队列中的内容就是Producer Batch,即Deque<ProducerBatch>。消息写入缓存时,追加到双端队列的尾部:Sender读取消息时,从双端队列的头部读取。注意ProducerBatch不是ProducerRecord, ProducerBatch 中可以包含一至多个ProducerRecord。通俗地说,ProducerRecord是生产者中创建的消息,而Producer Batch是指一个消息批次,ProducerRecord会被包含在ProducerBatch中,这样可以使宇节的使用更加紧凑。与此同时,将较小的ProducerRecord拼凑成一个较大的ProducerBatch,也可以减少网络请求的次数以提升整体的吞吐量。如果生产者客户端需要向很多分区发送消息,则可以将buffer.memory参数适当调大以增加整体的吞吐量。

消息在网络上都是以字节(Byte)的形式传输的,在发送之前需要创建一块内存区域来保存对应的消息。在Kafka生产者客户端中,通过java.io.ByteBuffer实现消息内存的创建和释放。不过频繁的创建和释放是比较耗费资源的,在RecordAccumulator的内部还有一个BufferPool,它主要用来实现ByteBuffer的复用,以实现缓存的高效利用。不过BufferPool只针对特定大小的ByteBuffer进行管理,而其他大小的ByteBuffer不会缓存进Bu他rPool中,这个特定的大小由batch.size参数来指定,默认值为16384B,即16KB。我们可以适当地调大batch.s工ze参数以便多缓存一些消息。

ProducerBatch的大小和batch.size参数也有着密切的关系。当一条消息(ProducerRecord) 流入RecordAccumulator时,会先寻找与消息分区所对应的双端队列(如果没有则新建),再从这个双端队列的尾部获取一个ProducerBatch(如果没有则新建),查看ProducerBatch中是否还可以写入这个ProducerRecord,如果可以则写入,如果不可以则需要创建一个新的Producer Batch。在新建ProducerBatch时评估这条消息的大小是否超过batch.size参数的大小,如果不超过,那么就以batch.size参数的大小来创建ProducerBatch,这样在使用完这段内存区域之后,可以通过BufferPool的管理来进行复用;如果超过,那么就以评估的大小来创建ProducerBatch,这段内存区域不会被复用。

Sender从RecordAccumulator中获取缓存的消息之后,会进一步将原本<分区,Deque<Producer Batch>>的保存形式转变成<Node,List< ProducerBatch>的形式,其中Node表示Kafka集群的broker节点。对于网络连接来说,生产者客户端是与具体的broker节点建立的连接,也就是向具体的broker节点发送消息,而并不关心消息属于哪一个分区;而对于KafkaProducer的应用逻辑而言,我们只关注向哪个分区中发送哪些消息,所以在这里需要做一个应用逻辑层面到网络1/0层面的转换。

在转换成<Node,List<ProducerBatch>>的形式之后,Sender还会进一步封装成<Node,Request>的形式,这样就可以将Request请求发往各个Node了,这里的Request是指Kafka的各种协议请求,对于消息发送而言就是指具体的ProduceRequest

请求在从Sender线程发往Kafka之前还会保存到InFlightRequests中,InFlightRequests保存对象的具体形式为Map<Nodeld,Deque>,它的主要作用是缓存了已经发出去但还没有收到响应的请求(Nodeld是一个String类型,表示节点的id编号)。与此同时,InFlightRequests还提供了许多管理类的方法,并且通过配置参数还可以限制每个连接(也就是客户端与Node之间的连接)最多缓存的请求数。这个配置参数为max.i n.flight.requests.per. connection,默认值为5,即每个连接最多只能缓存5个未响应的请求,超过该数值之后就不能再向这个连接发送更多的请求了,除非有缓存的请求收到了响应(Response)。通过比较Deque的size与这个参数的大小来判断对应的Node中是否己经堆积了很多未响应的消息,如果真是如此,那么说明这个Node节点负载较大或网络连接有问题,再继续向其发送请求会增大请求超时的可能。


自述:

参考地址

第一步:一条消息过来首先会被封装成一个 producerRecord 对象
第二步:消息可能会经过拦截器(拦截器可以不指定)
第三步:经过序列化器
第四步:经过分区器
第五步:发送过来的数据都会被追加到RecordAccumulator(消息累加器:主要用来缓存消息以便sender线程可以批量发送,该消息累加器默认的缓存大小为32MB)的某个双端队列(Deque)中,队列中的内容就是 ProducerBatch(消息批次,默认大小是16K,ProducerBatch中可以包含一个至多个ProducerRecord)。

消息写入缓存时,追加到双端队列的尾部:Sender读取消息时,从双端队列的头部读取;在 RecordAccumulator 的内部还有一个bufferPoll,主要用来实现 byteBuffer的复用,当batch.size 或 linger.ms 符合条件的时候,sender 线程从RecordAccumulator中读取数据,将原本<分区,Deque<Producer Batch> 的形式转换为 <node,list>,node表示kafak集群的broker节点,生产者客户端是与具体的broker节点建立连接也就是向具体的broker节点发送消息,之后转换为 <Node,Request>的形式,这样就可以将Request请求发送到各个node上,请求在从sender线程发往kafka之前还会保存到 InFlightRequests 中,InFlightRequests保存对象的具体形式为 Map<Nodeld,Deque> 的主要作用是缓存了已经发出去但还没有收到响应的请求(nodeid表示节点的id编号),通过(max.in.flight.requests.per.connection)参数设置可以限制每个连接最多缓存的请求数,默认值为5.每个连接最多只能缓存5个未相应的请求超过该数据之后不能再向这个连接发送更多的请求。
broker节点收到数据之后开始进行副本的同步,同步完成之后进行acks的应答三种级别:0,1,-1(all)应答如果成功清理掉对应的请求,同时清理掉双端队列中的数据。应答如果失败的情况下进行重试机制(默认重试的次数时int 的最大值)直到它发送成功为止

分区

在某些场景下,需要控制每条消息落到合适的分区中,在kafkaProduce计算分配时,首先根据的是ProducerRecord中的partition字段指定的序号计算分区。

概念

  • 将每个Topic划分成多个分区(Partition),每个分区是一组有序的消息日志,生产者生产的每条消息只会被发送到其中一个分区。
  • 提高并行度,生产者和和消费者可以以分区为单位消费和发送数据,一个分区只能被一个消费组下的一个消费者消费,每个分区是一个有序不可变的数据序列(区内有序),消息数据会被不断的添加到序列的尾部。分区中的每条数据都有一个偏移量(offset),用于唯一标识分区中的每条消息数据。
  • 分区的作用就是提供负载均衡的能力,单个topic的不同分区可存储在相同或不同节点机上,为实现系统的高伸缩性(Scalability),不同的分区被放置到不同节点的机器上,各节点机独立地执行各自分区的读写任务,如果性能不足,可通过添加新的节点机器来增加整体系统的吞吐量。

副本

副本相关的概念:AR,ISR,HW,LEO

每个分区的 leader 会维护一个 in-sync replica(同步副本列表,又称 ISR)。当 producer 往 broker 发送消息,消息先写入到对应 leader 分区上,然后复制到这个分区的所有副本中。只有将消息成功复制到所有同步副本(ISR)后,这条消息才算被提交。

正常情况下,分区的所有副本都处于ISR集合中,但是难免会有异常情况发生,从而某些副本被剥离出ISR集合中。在ISR集合之外,也就是处于同步失效或功能失效(比如副本处于非存活状态)的副本统称为失效副本,失效副本对应的分区也就称为同步失效分区,即under-replicated分区。

当ISR集合中的一个follower副本滞后leader副本的时间超过此参数指定的值时则判定为同步失败,需要将此follower副本剔除出ISR集合

副本选举机制(待总结)

每个分区的 leader 会维护一个 ISR 列表,ISR 列表里面就是 follower 副本的 Borker 编号,只有跟得上 Leader 的 follower 副本才能加入到 ISR 里面,只有 ISR 里的成员才有被选为 leader 的可能。当 Leader 挂掉的情况下Kafka 会从 ISR 列表中选择第一个 follower 作为新的 Leader。

leader选举流程

leader 和 follower 故障处理

follower 故障

LEO(Log End Offset):每个副本的最后一个offset,LEO其实就是最新的offset + 1。

HW(High Watermark):所有副本中最小的LEO 。


image-20220811120502639
  1. Follower发生故障后会被临时踢出ISR
  2. 这个期间Leader和Follower继续接收数据
  3. 待该Follower恢复后,Follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向Leader进行同步。
  4. 等该Follower的LEO大于等于该Partition的HW,即Follower追上Leader之后,就可以重新加入ISR了。
leader 故障

LEO(Log End Offset):每个副本的最后一个offset,LEO其实就是最新的offset + 1

HW(High Watermark):所有副本中最小的LEO


image-20220811120605735
  1. Leader发生故障之后,会从ISR中选出一个新的Leader
  2. 为保证多个副本之间的数据一致性,其余的Follower会先将各自的log文件高于HW的部分截掉,然后从新的Leader同步数据。

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

acks应答级别

这个参数用来指定分区中必须要有多少个副本收到这条消息,之后生产者才会认为这条消息是成功写入的。acks是生产者客户端中一个非常重要的参数,它涉及消息的可靠性和吞吐量之间的权衡。acks参数有3种类型的值(都是字符串类型)

  • 0 :producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据
  • 1 : producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据
  • -1(all): producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复

AR(Assigned Replicas)

  • 分区中所有的副本统称为AR

ISR(In-Sync Replicas)同步的副本

  • 所有与leader副本保持一定程度同步的副本(包括leader副本在内)组成ISR(In-Sync Replicas),ISR集合是AR集合中的一个子集。
  • Leader维护了一个动态的in-sync replica set (ISR),表示和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会给follower发送ack。如果follower长时间未向leader发送通讯请求或同步数据,就会将该follower踢出ISR。默认30s,leader发生故障之后,就会从ISR中选举新的leader

0SR( Out-of-Sync Replicas)不同步的副本

  • 与leader副本同步滞后过多的副本(不包括leader副本)组成OSR(Out-of-Sync Replicas)
  • 表示Follower与Leader副本同步时,延迟过多的副本。

总结

leader 副本负责维护和跟踪ISR集合中所有follower副本的滞后状态,当follower 副本落后太多或失效时,leader副本会把它从ISR集合中剔除。如果OSR集合中有follower副本“追上’p了leader副本,那么leader副本会把它从OSR集合转移至ISR集合。默认情况下,当leader副本发生故障时,只有在ISR集合中的副本才有资格被选举为新的leader,而在OSR集合中的副本则没有任何机会

分区中各个偏移量的说明

在这里插入图片描述

HW(High Watermark)

高水位,它标识了一个特定的消息偏移量(offset),消费者只能拉渠道这个 offset 之前的数据

上图所示:

​ 代表这个文件中有9条消息,第一条消息的offset(logStartOffset)为0。最后一条消息的偏移量为8,offset为9的消息用虚线框表示,代表下一条待写入的消息。文件中的HW为6,表示消费者只能拉取到offset在0-5之间的消息,而offset为6的消息对消费者而言是不可见的。

LEO(Log End Offset)

它标识当前日志文件中下一条带写入消息的offset,上图中offset为9的位置为当前日志文件的LEO,LEO的大小相当于当前日志分区中的最后一条消息的offset值加1,分区ISR集合中的每个副本都会维护自身的LEO,而ISR集合中最小的LEO即为分区的HW,对消费者而言只能消费HW之前的消息

理解ISR集合,以及HW和LEO之间的关系

如图所示:

在这里插入图片描述

如图1-5所示,假设某个分区的ISR集合中有3个副本,即一个leader副本和2个follower副本,此时分区的LEO和HW都为3。消息3和消息4从生产者发出之后会被先存入leader副本,如图1-6所示。

在这里插入图片描述

在消息写入leader副本之后,follower副本会发送拉取请求来拉取消息3和消息4以进行消息同步。

在同步过程中,不同的follower副本的同步效率也不尽相同。如图1-7所示,在某一时刻follower1完全跟上了leader副本而follower2只同步了消息3,如此leader副本的LEO为5,follower1的LEO为5, follower2的LEO为4,那么当前分区的HW取最小值4,此时消费者可以消费到offset为0至3之间的消息。

在这里插入图片描述

写入消息 如图1-8所示,所有的副本都成功写入了消息3和消息4,整个分区的HW和LEO都变为5,因此消费者可以消费到offset为4的消息了。

在这里插入图片描述

由此可见,Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。事实上,同步复制要求所有能工作的follower副本都复制完,这条消息才会被确认为已成功提交,这种复制方式极大地影响了性能。而在异步复制方式下,follower副本异步地从leader副本中复制数据,数据只要被leader副本写入就被认为已经成功提交。在这种情况下,如果follower副本都还没有复制完而落后于leader副本,突然leader副本着机,则会造成数据丢失。Kafka使用的这种ISR的方式则有效地权衡了数据可靠性和性能之间的关系。

压缩类型

目前 Kafka 共支持四种主要的压缩类型:Gzip、Snappy、Lz4 和 Zstd。关于这几种压缩的特性

压缩类型压缩比率CPU 使用率压缩速度带宽使用率
GzipHighestHighestSlowestLowest
SnappyMediumModerateModerateMedium
Lz4LowLowestFastestHighest
ZstdMediumModerateModerateMedium

消费者

消费方式

  • pull (拉)模式:

    kafka采用pull的模式从broker中主动拉取数据

    pull模式不足之处是,如果Kafka没有数据,消费者可能会陷入循环中,一直返回空数据。

  • push(推)模式:

    卡夫卡没有采用这种方式,因为有broker决定消息发送速率,很难适应所有消费者的消费速率

位移提交 (offset)提交

在老版本中 消息位移是存储在zookeeper中的,在新版本中消费位移存储在kafka内部的主题中(_consumer _offsets)

位移提交象征着Consumer 的消费进度,当Consumer 发生故障重启之后,就能够从kafka中读取之前提交的位移值,然后从相应的位移处继续消费数据,从而避免整个消费过程重来一遍。

从用户的角度来说,位移提交分为 手动提交和自动提交;

在这里插入图片描述

自动提交

  • 自动提交参数
    • enable.auto.commit (boolean) :如果为 true,消费者的偏移量将在后台定期提交。kafka会保证在开始调用poll方法时,提交上次poll返回的所有消息。从顺序上来说,poll方法的逻辑是在提交上一批消息的位移,在处理下一批消息,因此它能保证不出现消费丢失的情况。
    • auto.commit.interval.ms(int) :自动提交offset的时间间隔(消费者偏移量自动提交到kafka的频率)

手动提交

手动提交offset的方法有两种:分别是commitSync(同步提交)和commitAsync(异步提交)。两者的相同点是,都会将本次提交的一批数据最高的偏移量提交;不同点是,同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故有可能提交失败。

  • commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。

  • commitAsync(异步提交):发送完提交offset请求后,就开始消费下一批数据了。

手动同步提交

由于同步提交offset有失败重试机制,故更加可靠,但是由于一直等待提交结果,提交的效率比较低。

手动异步提交

虽然同步提交offset更可靠一些,但是由于其会阻塞当前线程,直到提交成功。因此吞吐量会受到很大的影响。因此更多的情况下,会选用异步提交offset的方式。

poll

poll 方法的返回值是ConsumerRecords,它用来表示一次拉取操作所获得的消息集,内部包含了若干个ConsumerRecord,提供了一个 iterator()方法来循环遍历消息集内部的消

消费者组

消费者组由多个消费者组成,形成一个消费者组的条件,所有的消费者的groupid相同。

  1. 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费

  2. 消费者之间互不影响。所有的消费者都属于某个消费者组

    在这里插入图片描述

  3. 如果向消费者组中添加多个消费者,超过主题分区数量,则有一部分消费者就会闲置,不会接受任何消息

    在这里插入图片描述

kafka 分区分配策略

  • 轮询分区分配策略(RoundRobinAssignor)

    将消费组内所有消费者及消费者订阅的所有主题的分区按照字典序排序,然后通过轮询方式逐个将分区依次分配给每个消费者。

  • 范围分区分配策略(RangeAssignor)

    按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配。保证分区尽可能均匀的分配给所有的消费者。

  • 粘性分区分配策略(StickyAssignor)

    StickyAssignor分配策略,它主要有两个目的:

    • 分区的分配要尽可能均匀。
    • 分区的分配尽可能与上次分配的保持相同。

kafka工作流程以及文件存储机制

在这里插入图片描述

Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。

topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个log文件。Producer生产的数据会被不断追加到该log文件末端,且每条数据都有自己的offset。消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。

在这里插入图片描述

生产者生产的消息会不断追加到log文件末尾,为防止log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment。每个segment对应两个文件——“.index”文件和“.log”文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号。

在这里插入图片描述

“.index”文件存储大量的索引信息,“.log”文件存储大量的数据,索引文件中的元数据指向对应数据文件中message的物理偏移地址。

Zookeeper在Kafka中的作用

Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。

Controller的管理工作都是依赖于Zookeeper的。

Kafka 高效文件存储设计特点

  • Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
  • 通过索引信息可以快速定位message和确定response的最大大小。
  • 通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
  • 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小

kafka再平衡

在平衡是指分区的所属权从一个消费者转移到另一个消费者的行为,它为消费者组具备高可用性和伸缩性提供保障,使我们可以既方便又安全的删除消费者内的消费者,或往消费组内添加消费者。在均衡发生期间,消费者内的消费者是无法读取消息的。也i就是说,在再均衡发生期间的这一段时间内消费者组变得不可用。另外,当一个分区被重新分配给另一个消费者时,消费者当前的状态也会丢失。比如:消费者消费完某个分区的一部分消息时还没来得及提交offset(消费位移)就发生了再均衡操作,之后这个分区又被分配给了消费组内的另一个消费者,原来被消费完的那部分消息又被重新消费一遍,也就是发生了重复消费。

一般会有三种情况会触发再平衡:

  • 有新的消费者加入消费组。
  • 有消费者若机下线。消费者并不一定需要真正下线,例如遇到长时间的GC、网络延迟导致消费者长时间未向GroupCoordinator发送心跳等情况时,GroupCoordinator会认为消费者己经下线。
  • 有消费者主动退出消费组(发送LeaveGroupRequest请求)。比如客户端调用了unsubscrible()方法取消对某些主题的订阅。
  • 消费组所对应的GroupCoorinator节点发生了变更。
  • 消费组内所订阅的任一主题或者主题的分区数量发生变化。

kafka再平衡策略

Kafka提供的再平衡策略主要有三种:Round RobinRangeSticky,默认使用的是Range

这三种分配策略的主要区别在于:

  • Round Robin:会采用轮询的方式将当前所有的分区依次分配给所有的consumer;
  • Range:首先会计算每个consumer可以消费的分区个数,然后按照顺序将指定个数范围的分区分配给各个consumer;
  • Sticky:这种分区策略是最新版本中新增的一种策略,其主要实现了两个目的:
  • 将现有的分区尽可能均衡的分配给各个consumer,存在此目的的原因在于Round RobinRange分配策略实际上都会导致某几个consumer承载过多的分区,从而导致消费压力不均衡;
  • 如果发生再平衡,那么重新分配之后在前一点的基础上会尽力保证当前未宕机的consumer所消费的分区不会被分配给其他的consumer上;

漏消费和重复消费

漏消费

设置offset为手动提交,当offset被提交时,数据还在内存中未落盘,此时刚好消费者线程挂掉,那么offset已经提交,但是数据并没有消费,导致内存中的这部分数据丢失。

在这里插入图片描述

重复消费

已经消费数据,但offset没有提交

在这里插入图片描述

场景一:重复消费自动提交offset引起

场景二:如果提交offset后的2s,consumer挂掉了

场景三:再次重启consumer,则从上一次提交的

解决重复消费和漏消费

需要kafak消费者将消费过程和提交offset过程做原子性绑定

在这里插入图片描述

kafka 数据挤压(消费者如何提高吞吐量)

  1. 如果消费能力不足,可以考虑增加topic的分区数,同时提升消费者数量,消费者数量=分区数
  2. 如果是下游的数据处理不过来的情况下,可以提高每批次拉取的数量,批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。在这里插入图片描述

kafka如何保证数据不丢失

  • 生产者:

    • 增大失败重试次数

    • 设置 acks 默认为1 可以设置为 all

  • 消费者:

    • 关闭offset自动提交,使用自动提交offset

    • 幂等性

kafka 高性能原因

  1. 分布式存储架构
  2. 磁盘顺序读写
  3. 读写数据的批量处理以及压缩传输
  4. 数据传输的零拷贝

kafka 事务和幂等性

事务

幂等

单地说就是对接口的多次调用所产生的结果和调用一次是一致的。生产者在进行重试的时候有可能会重复写入消息,而使用Kafka的幕等性功能之后就可以避免这种情况。

Kafka的幂等性机制能保证单个分区不会重复写入数据,而实现幂等性的核心就是引入了producer id 和 sequence number这两个概念。

Kafka内部会自动为每个Producer分配一个producer id(PID),broker端会为producer每个Partition维护一个<PID,Partition> -> sequence number映射。sequence number时从0开始单调递增的。

对于新接受到的消息,broker端会进行如下判断:

  1. 如果新消息的sequence number正好是broker端维护的<PID,Partition> -> sequence number大1,说broker会接受处理这条消息。
  2. 如果新消息的sequence number比broker端维护的sequence number要小,说明时重复消息,broker可以将其直接丢弃
  3. 如果新消息的sequence number比broker端维护的sequence number要大过1,说明中间存在了丢数据的情况,那么会响应该情况,对应的Producer会抛出OutOfOrderSequenceException。

页缓存

将磁盘中的数据缓存到内存中,当一个进程准备读取磁盘上的文件内容时,操作系统会先查看待读取的数据所在的页(page)是否在页缓存(pagecache)中,如果存在(命中〉则直接返回数据,从而避免了对物理磁盘的I/O操作;如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据页存入页缓存,之后再将数据返回给进程。同样,如果一个进程需要将数据写入磁盘,那么操作系统也会检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中添加相应的页,最后将数据写入对应的页。被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。

零拷贝

零拷贝是指将数据直接从磁盘文件复制到网卡设备中,而不需要经由应用程序。减少了内核和用户模式之间的上下文切换

kafka 文件存储机制

Topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是Producer生产的数据。Producer生产的数据会被不断追加到该log文件末端,为防止log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment。每个segment包括:“.index”文件、“.log”文件和.timeindex等文件。这些文件位于一个文件夹下,该文件夹的命名规则为:topic名称+分区序号,例如:first-0。

在这里插入图片描述

在这里插入图片描述

如何获取 topic 主题的列表

bin/kafka-topics.sh --list --zookeeper localhost:2181

生产者和消费者的命令行是什么?

生产者在主题上发布消息:

bin/kafka-console-producer.sh  --broker-list localhost:9092 --topic first

注意这里的 IP 是 server.properties 中的 listeners 的配置。接下来每个新行就是输入一条新消息。
消费者接受消息:

bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic first

创建 topic

bin/kafka-topics.sh --zookeeper localhost:2181 --create --replication-factor 3 --partitions 1 --topic first

Kafka 判断一个节点是否还活着有那两个条件?

(1)节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接
(2)如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久

Kafka中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?

拦截器 -> 序列化器 -> 分区器

Kafka Controller的作用?

负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。

Kafka中有那些地方需要选举?这些地方的选举策略又有哪些?

partition leader(ISR),controller(先到先得)

Kafka有内部的topic吗?如果有是什么?有什么所用?

__consumer_offsets,保存消费者offset

topic的分区数可不可以增加?如果可以怎么增加?如果不可以,那又是为什么?

可以增加分区数
bin/kafka-topics.sh --zookeeper localhost:2181/kafka --alter --topic topic-config --partitions 3

topic的分区数可不可以减少?如果可以怎么减少?如果不可以,那又是为什么?

不可以减少,被删除的分区数据难以处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值