文章目录
1. Kafka 概述
Kafka是Apache开发的,一个分布式的基于发布/订阅
模式的消息队列(Message Queue),主要应用于大数据实时处理领域。
1.1. Kafka 特点
- 作为
消息队列
的特点
-
服务解耦
消息队列连接的两端,只需要关注消息入参和结果,中间过程相关性不强,易于调整修改。
-
可恢复性
消息队列连接的两端,一端宕机,只要消息队列正常,则等待宕机一端回复还能继续处理上一个消息。
-
缓冲
控制和优化数据流经过系统的速度,解决
生产消息和消费消息的处理速度不一致
的情况。 -
异步通信
当消费端不希望立即处理消息,消息队列提供异步处理支持,即现将消息存储在消息队列,等消费端需要时再发送消息。
-
作为
Kafka
的特点-
发布订阅 模式
点对点模式:
发布订阅模式:
-
Kafka采用发布订阅模式,区别于点对点,发布订阅模式不是单一的点对点即一对一消费数据,而是可以将消息发送给多个消费者或消费者组,并且提供存储消息的功能。
-
高吞吐
单一的Kafka代理可以处理成千上万的客户端,
每秒处理数兆字节
的读写操作,Kafka性能远超过传统 的ActiveMQ、RabbitMQ等。Kakfa吞吐量大的主要原因如下:-
吞吐量大
为了获取更大的消息存储空间并保证数据可靠性,Kafka会将消息生成segment file持久化在磁盘中,磁盘空间大,故吞吐量大。以上是单节点吞吐,往往搭建集群。
-
吞吐效率高
-
顺序读写
kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能顺序读写
大大减少硬盘磁头的寻找址时间
,只需很少的扇区旋转时间,所以速度远快于随机读写。官网有数据表明,同样的磁盘,顺序写效率600M/s,随机写只有100K/s。
-
文件分段
kafka的队列topic被分为了多个区partition,每个partition又分为多个段
segment
,segment除了使用.log文件存储数据,还会生成.index索引文件。这样每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了 并行处理能力。文件存储机制详细见 [Kafka 文件存储机制](#2.1. Kafka 文件存储机制)。 -
PageCache 内存优化
Kafka使用
操作系统提供的PageCache
持久化数据,有如下优势:- 使用堆外内存,减少GC,提高效率。
- I/O Scheduler 将连续的小块物理写封装为大块的物理写。
- I/O Scheduler 在写数据前排序,以减少寻址时间。
- 一旦进程重启JVM Cache 失效,机器不重启,PageCache 不失效。
-
Batch发送
Batch发送指的是先将消息缓存在内存中,等消息大小或时间达到阈值默认16k
,将这部分数据批量发送出去,减少IO次数,从而提高效率。 -
数据压缩
Producer发送消息集合前,可以将消息集合通过 gzip或者snappy 压缩,减少网络IO成本。在Consumer消费数据时,进行解压,CPU换网络IO效率,适合大数据计算场景。 -
零拷贝
Linux会将内存分为供操作系统正常工作的kernel space
,以及以用户为单位供程序使用的User space
。以读取数据为例,先从磁盘中读取文件,再进入内核空间,Kakfa可能会读取 PageCache 中,然后再复制到用户空间的JVM Cache,中间出现多次复制。
零拷贝就是将文件从磁盘读取到 PageCache 后,不必再经过 User spase,直接落盘或者写给NIC(网卡),进行网络传输,减少了复制成本,从而提高效率。
-
-
-
扩展性强
Kafka集群可以透明的扩展,增加新的服务器进集群。
-
容错性高
Kafka每个Partition数据会复制到几台服务器,当某个Broker失效时,Zookeeper将通知生产者和消费者从而使用其他的Broker。
2. Kafka 架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yq2n4Puw-1646383090459)(kafka.assets/image-20220210095416927.png)]
-
Producer :消息生产者,就是向 kafka broker 发消息的客户端。
-
Consumer :消息消费者,向 kafka broker 读取消息的客户端。
-
Consumer Group (CG):消费者组,由多个consumer组成。
消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费
;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。 -
Broker :一台kafka服务器就是一个broker, 一个集群由多个broker组成, 一个broker可以容纳多个topic。
-
Topic :可以理解为一个队列,
生产者和消费者面向的都是一个topic
。 -
Partition:为了实现扩展性,一个非常大的topic可以分布到多个 broker(即服务器)上,
一个topic可以分为多个partition
,每个partition是一个有序的队列。topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个
log
(由于分片和索引机制物理存储有多个)文件,该log文件中存储的就是producer生产的数据。Producer生产的数据会被不断追加
到该log文件末端,且每条数据都有自己的offset
。消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。 -
Replica:副本,kafka提供了副本机制,一个topic的
每个分区都有若干个副本
,副本分为一个leader 和若干个follower
。 -
Leader:
生产者发送数据的对象以及消费者消费数据的对象都是leader
。 -
follower:实时从leader中同步数据,保持和leader数据的同步。leader发生故障时,某个follower会成为新的leader。
2.1. Kafka 文件存储机制
由于生产者生产的消息会不断追加到log文件末尾,为防止log文件过大导致数据定位效率低下,Kafka采取了 分片和索引
机制,将每个partition分为多个segment
。每个segment对应两个文件——.index和.log
文件,并以当前segment第一条数据的offset命名
,它们都位于 topic名称+分区序号
文件夹下。
- .log文件:存储大量数据。
- .index文件:存储大量元数据,即
索引编号 及 message物理偏移量
。
消费消息时,从 topic名称+分区序号
文件夹开始,首先定位开始消费的message的 offset 再哪个.index 文件范围内。定位到了之后,扫描定位到的 .index 文件,根据开始消费的message的 offset 定位到这条消息的索引信息,通过索引中保存的message物理偏移量,检索 .log 文件信息,完成消费数据。
2.2. Kafka 生产者
2.2.1. 分区策略
-
分区的原因
- 方便在集群中扩展,Partition可以负载均衡分布在各个节点,扩展性强。
- 可以提高并发,因为可以以Partition为单位进行读写了。
-
分区的原则
生产者生产的数据,会以
ProducerRecord
对象发送给Kafka,ProducerRecord推荐使用KV数据结构,但是也支持单Value类型。-
默认情况
- 指明 Partition,直接用。
- 未指明 Partition,有 Key,
hash取模
,取 key 的hash值 与 partition 数量取余。 - 未指明 Partition,无Key或者Key为null,2.4之前使用RoundRobin(
轮询
),2.4之后(我们用的就是Kafka2.4.1
)使用 Sticky Partition(黏性分区器
)。
-
RoundRobin(
轮询
)当生产者发送数据时,如果是Batch发送,先讲Batch尽量均分为Partition数量个批次,然后轮询发送将这些批次给每个Partition。
-
Sticky Partition(
黏性分区器
)当生产者发送数据时,如果是Batch发送,首先会先随机选择其中一个分片, 然后尽可能粘住这个分片, 将这一批数据全部交给这一个分片。好处是减少了切分批次,同时也减少了ack响应次数,提高效率。但是没有轮询分布均匀,需要全局调控,保证最终分布均衡。
-
自定义分区策略
- 实现Partition接口
- 实现 partition( ),close( )等方法。
-
2.2.2. 生产数据可靠性保证
-
生产者发送到 Partition 的可靠性保证
为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送
ack
(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否则重新发送
数据。
-
Partition 存储数据的可靠性保证
-
副本同步策略:
选择
全部完成同步,才发送ack
,保证Kafka容错性,且Kafka吞吐量大,延迟影响较小。
-
ISR
采用上述策略,很可能出现
某个副本节点故障无法同步成功,导致消息一致发送不成功
。
Leader维护了一个动态的in-sync replica set (ISR),意为和leader保持同步的follower集合
。当ISR中的follower完成数据的同步之后,leader就会给producer发送ack。如果follower长时间(阈值为 replica.lag.time.max.ms)未向leader同步数据,则该follower将被踢出ISR,Leader发生故障之后,就从ISR中选举新的leader。 -
ack应答级别
对数据可靠性不同的场景,可以使用不同的ack应答级别,由
acks
参数控制。-
acks=0
:partition的leader接收到消息
还没有写入磁盘
就已经返回ack,当leader故障时有可能丢失数据 -
acks=1
:
partition的leader落盘成功
后返回ack,如果在follower同步成功之前leader故障,可能丢失数据。
-
acks=-1(all)
:
partition的leader和follower全部落盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,会造成数据重复。
-
-
-
leader 和 follower 故障处理
LEO (log end offset):指的是每个副本最大的offset。
HW (high watermark):指的是消费者能见到的最大的offset,ISR队列中最小的LEO。
-
follwer 故障
follower发生故障后会被临时踢出ISR,待该follower恢复后,follower会
读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉
,从HW开始向leader进行同步。等该follower的LEO大于等于该Partition的HW
,即follower追上leader之后,就可以重新加入ISR了。 -
leader 故障
leader发生故障之后,会从ISR中选出一个新的leader,之后,为保证多个副本之间的数据一致性,
其余的follower会先将各自的log文件 高于HW的部分截掉
,然后从新的leader同步数据。
注意:这只能保证副本之间的数据一致性,并不能保证数据的
准确性
,即不丢失或者不重复。 -
-
保证生产数据 Exactly Only,详见[Exactly Once 生产](#2.2.4. Exactly Once 生产)。
2.2.4. Exactly Once 生产
设置ack应答级别acks=-1,实现At Least Once语义,只能保证数据不丢失,无法保证数据不重复。
设置ack应答级别acks=0或1,实现At Most Once语义。只能保证数据不重复,无法保证数据不丢失。
引入了幂等性,通过 At Least Once + 幂等性 实现 Exactly Once。
设置 acks=-1 和 enable.idempotence = ture 幂等性
。开启幂等性的Producer在初始化的时候会被分配一个PID生产者ID
,发往同一Partition的消息会附带PartitionID分区ID和Sequence Number序列号
,而Broker端会对<PID, PartitionID, Sequence Number>
做缓存,当具有相同主键的消息提交时,Broker只会持久化一条,即实现幂等性。
如果重启Producer,那么PID会变化,无法保证唯一键的唯一性,所以幂等性无法保证跨分区跨会话
的Exactly Once,解决方案详见 [Producer 事务](#3.1. Producer 事务)。
2.3. Kafka 消费者
2.3.1. 消费方式
consumer采用pull
(拉)模式从broker中读取数据。
- push 模式。push模式推送数据的速率由broker决定,一般是有多少推送多少,起不到缓冲的作用,不考虑消费者的消费能力,很容易造成消费更不上生产。
- pull模式。pull可以让消费者根据自己的消费能力,主动去拉取消息,自己控制消费速率。但是如果boker没有数据,那么消费者会陷入
pull循环
,比较消耗性能。Kafka的处理是加入一个timeout
,即如果 kafka 没有数据,消费者会在timout
时间后进行下一次 pull。
2.3.2. 分区分配策略
消费消息的逻辑单位为 comsumer group,一个 CG 包括一个或多个 consumer,往往一个topic的partition也是多个,topic的partitioin 分配给 comsumer 需要分区策略控制。
现在的分区分配策略有三种 Range(范围 默认
),RoundRobin(轮训
) 和 Sticky(粘性
)。
-
Range(范围)
Range
对每个 Topic 单独分配
,先按分区ID顺序对partition进行排序,按消费者ID对消费者排序,再将 partition 尽量平分分配给各个消费者,如果不能平分,优先分给消费者ID靠前的cusumer
。- 缺点:由于以 Topic 为分配单位,并且消费者编号靠前的 cusumer 被分配的机会更大。随着
消费者组订阅的Topic
越来越多,consumer 编号越靠前的会分到更多的 partition,造成分配不均,如下图:
- 缺点:由于以 Topic 为分配单位,并且消费者编号靠前的 cusumer 被分配的机会更大。随着
-
RoundRobin(轮询)
RoundRobin对消费者组订阅的所有 Topic 统一分配
,再将 partition 以轮询
方式分配给各个消费者。-
优点:
消费者组内,所有消费者的最大partition相差数为1,消息分布均衡,负载均衡能力强。
-
缺点:
当消费者组中的cusumer增加或者减少时,
从头进行轮询分配
,这使得之前在某个cusumer中的partition很可能进入其他的cusumer,对于消费者来说成本很大。
-
-
Sticky(粘性)
Sticky在RoundRobin的基础上,在partition重分配过程做出了优化,遵循一下原则:
- 分区的分配尽量的均衡。
- 保证第一点的情况下,重分配结果尽量与上一次分配结果保持一致。
Sticky的特点在
减少消费者组的cusumer
情况下最显著,比如某一个consumer下线,那么Sticky会保证其他的consumer原本的partition分配不变,然后再将下线的cusumer的partition尽量均衡地分配。
2.3.3. offset 的维护
consumer在消费数据的过程中,可能出现宕机下线等问题,而consumer要想恢复后继续消费下线前的数据,就需要找到下线前的 offset。由于offset 对可靠性和实时性的要求很高,所以需要一个实时高效的组件辅助存储。
kafka0.9版本之前
: consumer 将 offset 存储在 zookeeper中,实时对offset进行读写操作。但是 zookeeper作为外部框架,一来是有网络IO开销成本;二来负载能力是kafka无法控制的,如果部署吞吐量极大的Kafka集群,但是zookeeper集群较小或者负载能力过大,就会拖累 kafka 的消费效率。
kafka0.9版本之后
: 考虑到以上原因,Kafka将cusumer的offset保存在 Kafka 自身维护的 Topic - __consumer_offsets
中。
2.3.4. 消息消费可靠性保证
-
手动提交 offset
Kafka将消息发送给消费者,如果消费者读取到了消息,那么 默认自动提交offset(即
先提交后处理
),最常见的消费者丢失消息的场景就是,自动提交offset之后,消费者还没来的及处理就宕机了,消费者重启之后,保存offset的服务返回的offset是上一次未处理的下一个数据,造成消息丢失。这就需要关闭自动提交offset,实现手动提交offset
(先处理后提交),先处理完逻辑,再手动提交offset。 -
消费者事务
实现了手动提交offset后,如果消费者刚处理完业务逻辑,手动提交offset前宕机。等到消费者重启时,存储offset的服务上一次未收到提交信息,会返回上一次的offset,消费者将重复处理消息。
所以,Kafka只能做到尽量不丢数据,无法保证数据重复,要想实现消费数据 Exactly Only,需要消费者自己做好幂等性。另外,消费者可靠性处理 详见 [消费者事务](#3.2. Consumer 事务)。
3. Kafka 事务
Kafka从0.11
版本开始引入了事务支持。事务可以保证Kafka在Exactly Once语义的基础上,生产和消费可以跨分区和会话
,要么全部成功,要么全部失败。
3.1. Producer 事务
Kafka生产数据,通过 acks=all + 消息幂等性
实现了 Exactly Only 精确一次性生产,但是由于 幂等性 唯一键 (PID,PartitionID,SequenceNumber
),PID是会话内有效的,即重启之后会改变,导致无法跨会话实现 Exactly Only。
Producer开启事务,会启动一个新的组件 Transaction Coordinator(事务协调器
)。事务协调器会生成一个 TransactionID 并且和 Producer的PID绑定,并且将这对ID保存在Kafka的一个Topic
中。如果Producer宕机,那么事务无法提交,TransactionID会保存下来,一旦 Producer 重启,就可以根据 TransactionID 找回PID,继续事务。如果超过事务处理时间阈值,则回滚事务。
Producer 事务核心就是使用 事务协调者 组件将PID和事务ID绑定,重启后的Producer可以终会事务ID,那么就相当于找回了宕机前的PID生产者ID,也就找回了 幂等性唯一键 PID,PartitionID,SequenceNumber
,完成跨会话的 Exactly Once 生产。
3.2. Consumer 事务
Consumer 事务需要 Kafka 下游的 Consumer 自己实现事务,涉及到分布式事务,可以参考 2PC 二阶段提交
。
4. Zookeeper 作用
Kafka集群中有一个broker会被选举为Controller
,Controller的管理工作都是依赖于Zookeeper的负责,Zookeeper干预管理集群broker的上下线
,所有topic的分区副本分配和leader选举
等工作。
Kafka 知识点
Kafka监控系统
Eagle。