Kafka笔记

目录

前言

生产者

消息发送原理

生产者如何提高吞吐量

分区策略

数据可靠性

数据重复性

幂等

事务

数据顺序 

Broker

工作流程

副本

Leader选举流程

故障处理

文件存储

文件存储机制

文件清理策略

高效读写数据 

PageCache

 零拷贝

消费者

消费者组 

 消费者组初始化流程

消费者组消费流程

分区分配策略

Range

 RoundRobin

Sticky

CooperativeSticky

Rebalance重平衡


前言

kafka3.x版本

生产者

消息发送原理

生产者如何提高吞吐量

  1. linger.ms:默认为0ms即无延迟发送(batch.size参数无效)。更大的linger.ms使producer等待更长的时间才发送消息,这样就能够缓存更多的消息填满batch进而提升tps,但是会增加消息的延时。
  2. batch.size:默认16K。更大的batch.size可以使更多的消息封装进同一个请求中,减少总请求数。
  3. compression.type:默认为none即不进行压缩。对消息进行压缩可以减小数据量,提升吞吐量,但是会加大producer端的cpu开销。
  4. buffer.memory:默认为32MB。当缓冲区被填满后producer立即进入阻塞状态直到有空闲内存被释放出来。阻塞时间一旦超过max.block.ms(默认1分钟)就会抛出TimeoutException。如果经常发生超时则要调大buffer.memory的值降低阻塞。

分区策略

kafka的默认分区器为DefaultPartitioner,官方对其解释如下:

The default partitioning strategy:
If a partition is specified in the record, use it
If no partition is specified but a key is present choose a partition based on a hash of the key
If no partition or key is present choose the sticky partition that changes when the batch is full. See KIP-480 for details about sticky partitioning.

默认分区策略:

1.如果指定了分区,那么就使用它。

2.如果没指定分区但设置了key则根据key进行hash后对分区数进行取模

3.如果没指定分区和key则按照粘性分区策略,当这批次数据满时进行下一分区的发送

数据可靠性

ack为0时(可靠性差,效率高):

ack为1时(可靠性中等,效率中):

 

ack为-1时(可靠性高,效率低): 

 

在ack设置为-1时,当leader收到数据后follower开始同步数据时假设有一个follower宕机了无法同步,那leader会进行ack应答吗?

针对这问题,在leader中维护了一个动态的ISR(in-sync replica set),意为和leader保持同步的leader+follower集合。

如果follower长时间未向leader发送通信请求或同步数据,则该follower将被踢出ISR。该时间的阈值由replica.lag.time.max.ms参数控制,默认30s。

值得一提的是如果分区副本数为1或者ISR应答的最小副本数量(由min.insync.replicas参数控制)为1,则和ack=1的效果是一样的。

那么如果需要数据完全可靠,那么需要如下条件:

ACK级别设置为-1 + 分区副本数大于等于2 + ISR里应答的最小副本数量大于等于2。

数据重复性

 在生产者方,想要保证exactly once,就得介绍一下幂等性和事务。

幂等

通过幂等想要做到exactly once,那么必须有如下条件:

幂等性 + 至少一次(ack = -1 + 分区副本数>=2 + ISR最小副本数>=2)

在Kafka中,Producer 默认不是幂等性的,但我们可以配置参数(enable.idempotence:true)创建幂等性 Producer

判断重复数据的标准如下:

具有<PID,Partition,SeqNumber>相同主键的消息进行提交时,Broker只会持久化一条。

PID:Kafka每次重启都会分配一个新的

Partition:分区号

SeqNumber:单调递增

由以上3个参数可以看出,幂等性只能保证单会话、单分区的exactly once。

事务

开启事务的前提必须开启幂等。

数据顺序 

  1. kafka在1.x版本之前需设置max.in.flight.requests.per.connection=1能保证单分区有序。该参数指定了生产者在收到服务器响应之前可以发送多少个消息。它的值越高,就会占用越多的内存,不过也会提升吞吐量。把它设为 1 可以保证消息是按照发送的顺序写入服务器的,即使发生了重试。
  2. 在1.x版本之后,未开启幂等时,max.in.flight.requests.per.connection必须设置为1才能保证单分区有序;开启幂等后,max.in.flight.requests.per.connection需要设置<=5(在kafka1.x以后,在启用幂等的前提下kafka服务端会缓存producer发来的最近5个request的元数据,并会在服务端重新排序,故能保证单分区有序)。

Broker

工作流程

broker的工作流程如下: 

  1. broker节点启动后向zk进行注册(注册broker id信息)。
  2. 向zk中注册controller(每个broker节点都有属于自己的controller,谁先注册谁就是controller leader)。 
  3. 由选举出来的controller leader监听brokers节点变化。
  4. controller leader进行分区leader副本的选举(选举规则为:在ISR中存活为前提,按照AR中排在前面的优先。例如AR[1,0,2],ISR[1,0,2],那么leader就会按照1,0,2的顺序轮询)。
  5. Controller leader将信息上报给zk。
  6. 其他controller从zk同步相关信息。(防止controller leader宕机导致数据丢失)

假设leader副本所在的broker节点挂了之后,controller leader在监听到节点变化后会重新进行leader选举,然后上报到zk更新leader以及ISR信息。

副本

  Kafka 分区中的所有副本统称为 AR(ISR + OSR)。 
ISR:表示和 Leader 保持同步的 Follower 集合。如果 Follower 长时间未向 Leader 发送
通信请求或同步数据,则该 Follower 将被踢出 ISR 。该时间阈值由 replica.lag.time.max.ms
参数设定,默认 30s Leader 发生故障之后,就会从 ISR 中选举新的 Leader
OSR: 表示 Follower Leader 副本同步时,延迟过多的副本。

Leader选举流程

Kafka 集群中有一个 broker 的 controller 会被选举为 controller leader,负责管理集群
broker 的上下线,所有 topic 的分区副本分配和 Leader 选举等工作。
选举规则:在ISR中存活为前提,按照AR中排在前面的优先。

故障处理

Leader故障处理

 Follower故障处理

文件存储

文件存储机制

kafka采取了分片索引机制,将每个partition分为多个segment。每个segment包含:".index"文件、".log"文件和".timeindex"等文件。

log和index文件都是以当前文件中最小的偏移量值进行命名的。索引文件中的元数据对应数据文件中消息的物理偏移地址。在查找消息的时候会根据offset值定位到相应segment的索引文件,然后在索引文件中找到小于等于offset的最大索引记录,通过该索引记录的position值在log文件中进行向后顺序遍历查找到对应的message。

注意:index文件中并没有为数据文件中的每条消息都建立索引,而是采用了稀疏索引的方式大约每往log文件写入4kb数据时会建立一条索引(log.index.interval.bytes参数可控制,默认4kb)。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的message不能一次定位到其所在数据文件的位置,从而需要做一次顺序扫描,但是顺序扫描的范围会变得很小。

文件清理策略

kafka中默认的日志保存时间为7天,如需修改日志保存时间,可使用如下参数:

  • log.retention.hours: 小时
  • log.retention.minutes: 分钟。
  • log.retention.ms: 毫秒。
优先级从低到高,检查是否到期由 log.retention.check.interval.ms参数控制,默认5分钟。
kafka中具体删除过期日志的策略如下:

delete日志删除(默认)

配置参数为:log.cleanup.policy = delete(所有数据启用删除策略)

  • 基于时间:默认打开 以 segment 中所有记录中的最大时间戳作为该文件时间戳。
  • 基于大小:默认关闭 。超过设置的所有日志总大小,删除最早的 segment
    log.retention.bytes ,默认等于 -1 ,表示无穷大。

compact日志压缩

配置参数为:log.cleanup.policy = compact(所有数据启用压缩策略)

对于相同key值的数据,只会保留最后一个版本 。压缩后的offset可能是不连续的,当从某个不存在的offset消费消息时,将会拿到比这个offset大的offset对应的消息并从这个位置开始消费。

高效读写数据 

  1. 分布式集群 + 分区,并行度高。
  2. 文件采取分片和索引机制,利用稀疏索引避免占用过多空间且又能快速定位具体的消息。
  3. 顺序写磁盘,在producer发送消息时是一直以追加的形式添加到文件末端。
  4. 页缓存 + 零拷贝。

PageCache

PageCache是系统级别的缓存,它把尽可能多的空闲内存当作磁盘缓存使用以此来提高IO效率。

当上层有写操作时,操作系统只是将数据写到PageCache中,同时标记Page的熟悉为dirty(脏页),待合适的时机再将脏页进行落盘;当有读操作时,会先从PageCache中查找数据,发生缺页才会进行磁盘调度。

在kafka的producer发送消息到broker后,消息并不是直接落盘的,而是直接进到PageCache中,PageCache中的数据会被内核的处理线程在合适的时机进行落盘操作。

consumer在消费消息时,会先从PageCache中获取消息,获取不到才会到磁盘中读取,并且会预读出相邻的块放入PageCache中。

 零拷贝

wiki中对零拷贝有如下定义:

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another.

从wiki的定义中,我们看到"零拷贝"是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space),而是直接在内核空间(Kernel Space)中传输到网络。

传统的网络IO步骤如下:

  1. 调用 read() 时,操作系统从磁盘中读取数据复制到内核空间的reader buffer
  2. cpu控制将内核空间的数据复制到用户空间的缓冲区
  3. 调用 write() 时,将用户空间的数据复制到内核空间的socket buffer
  4. 将内核空间的socket buffer的数据复制到网卡设备中进行发送

可以发现,同一份数据需要进行4次copy。

通过SendFile系统调用进行优化之后,直接把数据从内核空间的read buffer拷贝到socket buffer,然后发送到网卡,避免了在内核空间与用户空间来回拷贝的弊端:

经过上述优化,数据只经过了2次copy就从磁盘发送出去了,并没有经过内核空间与用户空间的多余的拷贝过程。

消费者

消费者组 

消费者组由多个consumer组成。 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。

 消费者组初始化流程

注:_consumer_offsets为存储消费者消费的offset主题,分区数默认为50。 

消费者组消费流程

分区分配策略

Kafka有四种主流的分区分配策略: Range、RoundRobin、Sticky、CooperativeSticky。
可以通过配置参数partition.assignment.strategy修改分区的分配策略(默认策略是Range + CooperativeSticky)。其中CooperativeSticky是3.0开始才有的。

Range

 RoundRobin

轮询分区策略看似完美但是有个很大的弊端

例如:同一消费者组中,有3个消费者C0、C1和C2,他们共订阅了 3 个主题:t0、t1 和 t2,这 3 个主题分别有 1、2、3 个分区(即:t0有1个分区(p0),t1有2个分区(p0、p1),t2有3个分区(p0、p1、p2)),即整个消费者所订阅的所有分区可以标识为 t0p0、t1p0、t1p1、t2p0、t2p1、t2p2。具体而言,消费者C0订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2,最终分区分配结果如下:

消费者C0消费 t0p0
消费者C1消费 t1p0 分区
消费者C2

消费 t1p1、t2p0、t2p1、t2p2 分区

Sticky

粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,kafka官方对其解释如下:
Guarantees an assignment that is maximally balanced while preserving as many existing partition assignments as possible.
大概意思为分区分配会最大限度地进行平衡,同时会尽可能的保留现有分区的分配。

CooperativeSticky

CooperativeSticky和Sticky类似,只是支持了cooperative协议。

Rebalance重平衡

kafka触发rebalance的条件如下:

  • 消费者订阅的主题发生变化
  • 消费者数量发生变化
  • 主题的分区数量发生变化
  • 消费者被coordinator认为是​dead状态,这可能是由于消费者发送心跳超时(session.timeout.ms=45s)或者处理消息时间过长(max.poll.interval.ms=5分钟)

在kafka2.3之前,rebalance各种分配策略基本都是基于eager协议的(包括RangeAssignor,RoundRobinAssignor)。尽管StickyAssignor这个策略能够在consumer在rebalance后能够维持原有的分配方案,可惜的是这个分配策略依旧是在eager协议的框架之下,rebalance时仍然需要每个consumer都先放弃当前持有的资源(分区)。因此在kafka2.3版本时应用了cooperative协议。

具体eager协议和cooperative协议的区别可参考:

kafka 重平衡解决方案: cooperative协议和static membership功能详解 - 知乎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值