1. Kafka 概述
1.1. Kafka 特点 2. Kafka 架构
2.1. Kafka 文件存储机制2.2. Kafka 生产者
2.2.1. 分区策略2.2.2. 生产数据可靠性保证2.2.4. Exactly once 生产 2.3. Kafka 消费者
2.3.1. 消费方式2.3.2. 分区分配策略2.3.3. offset 的维护2.3.4. 消息消费可靠性保证 3. Kafka 事务
3.1. Producer 事务3.2. Consumer 事务 4. Zookeeper 作用Kafka 知识点
Kafka监控系统
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。
由于生产者生产的消息会不断追加到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( )等方法。
生产者发送到 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 生产)。
设置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端会对
如果重启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。
消费消息的逻辑单位为 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,造成分配不均,如下图:
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 事务)。
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。



