4.1 Kafka工作流程
Kafka中的消息是以topic
进行分类的,生产者生产消息,消费者消费消息,都是面向topic
的
topic
是逻辑上的概念,而partition
是物理上的概念,每个partition
对应一个log
文件,该log
文件中存储的信息就是producer
生产的数据,Producer
生产的数据会被不断的追加到该log
文件末尾,且每条数据都有自己的offset
(偏移量),消费者组中的每个消费者,都会实时记录自己消费到那个offset
(偏移量),以便出错恢复时,从上次的位置继续消费。
4.2 Kafka文件存储机制
由于生产者生产的消息会不断追加到log
文件末尾,为防止log
文件过大导致数据定位效率低下,Kafka
采用了分片和索引机制,将每个partition
分为多个segment
,每个segment
对应两个文件“xxx.index
”及“xxx.log
”文件,这些文件位于一个文件夹下,该文件夹的命名规则为topic
名称+分区序号,index
和log
文件以当前segment
的第一条消息的offset
命名。
“.index
”文件存储大量的索引信息,“.log
”文件存储大量的数据,索引文件中的元数据指向对应数据文件中message
的物理偏移地址。
4.3 生产者(Producer)
4.3.1 分区策略
1)分区的原因
- 方便在集群中扩展,每个
Partition
可以通过调整以适应它所在的机器,而一个topic
又可以有多个Partition
组成,因此整个集群就可以适应任意大小的数据了 - 可以提高并发,以
partition
为单位读写
2)分区的原则
我们需要将producer
发送的数据封装成一个ProducerRecord
对象
- 指明
partition
的情况下,直接将指明的值直接作为partition
值 - 没有指明
partition
值但存在key
的情况下,将key
的hash
值与topic
的partition
数进行取余得到partition
值 - 都不存在的情况下,第一次调用时随机生成一个整数,将这个值与
topic
可用的partition
总数取余得到partition
值,也就是常说的轮询算法。
4.3.2 数据可靠性保证
为保证producer
发送的数据,能可靠的发送到指定的topic
,topic
的每个partition
收到producer
发送的数据后,都需要向producer
发送ack
,如果peoducer
收到ack
,就会进行下一轮的发送,否则重新发送数据。
1)副本数据同步策略
- 半数以上完成同步,发送
ack
- 优点:延迟低
- 缺点:选举新的
leader
时,容忍n
台节点的故障,需要2n+1
个副本
- 全部完成同步,发送
ack
- 优点:选举新的
leader
时,容忍n
台节点的故障,需要n+1
个副本 - 缺点:延迟高
- 优点:选举新的
Kafka
采用第二种同步方案:其原因如下
- 同样为了容忍n台节点的故障,第二种需要的副本更少,而
kafka
的每个分区都会保存大量的数据,第一种就会造成大量的数据冗余 - 网络延迟对
kafka
的影响较小,可以不考虑
2)ISR
leader
维护了一个动态的in-sync replica set(ISR)
,意为和leader
保持同步的follower
集合,当ISR
中的follower
完成数据的同步之后,leader
就会给follower
发送ack
,如果follower
长时间未向leader
同步数据,则该follower
将被踢出isr
,该时间阈值由replica.lag.time.max.ms
参数设定,Leader
发生故障之后,就会从ISR
中选举新的Leader
3)ack
应答机制
对于某些不太重要的数据,对数据的可靠性要求不高,能容忍数据的少量丢失,所以没必要等ISR中的follower
全部接收成功
所以kafka
为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,用户可以选择以下配置:
acks
参数配置说明:
- 0:`producer`不等待`broker`的`ack`,这一操作提供了一个最低的延迟,`broker`一接收到还没写入磁盘就已经返回,当`broker`故障时有可能丢失数据。
- 1:`producer`等待`broker`的`ack`,`partition`的`leader`落盘成功后返回`ack`,如果在`follower`同步成功之前`leader`故障,那么将会丢失数据。
- all(-1):`producer`等待`broker`的`ack`,`partition`的`leader`和`follower`全部落盘成功后才返回`ack`,但是如果在`follower`同步完成后,`broker`发送`ack`之前,`leader`发生故障,那么会造成数据重复。
4)数据一致性
log
文件中包含HW
和LEO
属性
LEO
:每个副本的最后一个offset
HW
:所有副本中最小的LEO
,HW
之前的数据才对Consumer
可见
(1)follower
故障:follower
发生故障后会被踢出ISR
,待该follower
恢复后,follower
会读取本地磁盘记录的上次的HW
。并将log
文件高于HW
的部分截取掉,从HW
开始向leader
进行同步,等待follower
的LEO
大于等于该Partition
的HW
,即follower
追上leader
之后,就可以重新加入ISR
了。
(2)leader
故障:leader
发生故障之后,会从ISR
中选出一个新的leader
,之后,为保证多个副本之间的数据一致性,其余的follower
会先将各自的log
文件高于HW
的部分截掉,然后从新的leader
同步数据。
4.3.3 Exactly Once语义
当ack=all
时可以保证producer
与server
之间不会丢失数据,即At Least Once
语义,当ack=0
时,可以保证消息只会发送一次,即At Most Once
语义。
At Least Once
可以保证数据不丢失,但是不能保证数据不重复,相对的,At Most Once
可以保证数据不重复,但是无法确保数据不丢失,所以针对一些特殊的需求,kafka
引入了一个重大特性:幂等性
幂等性:指producer
无论向server
发送多少次重复数据,server
都只会持久化一条,幂等性结合At Least Once
语义就构成了Exactly Once
语义
即:Exactly Once
= At Least Once
+ 幂等性
4.4 消费者(Consumer)
4.4.1 消费方式
消费者采用pull
(拉)模式从broker
中读取数据
- `push`(推):此模式很难适应消费速率不同的消费者,因为消息发送速率是由`broker`决定的,它的目标是尽可能以最快速率传递消息,但这样容易造成消费者来不及处理消息,
- `pull`(拉):此模式可以适应消费速率不同的消费者,但是其不足之处是如果`kafka`没有数据,消费者可能会陷入循环中,一直返回空数据,针对此问题,`kafka`的消费者在消费数据时会传入一个时长参数timeout,如果没有数据可消费,消费者会等待一段时间在返回。
4.4.2 分区分配策略
一个consumer group
中包含多个consumer
,一个topic
有多个partition
,所以必然会涉及到partiton
的分配问题,即确定那个partition
由哪个consumer
来消费。
Kafka
有两种分配策略,一是RoundRobin
,一是Range
-
RoundRobin
:轮询策略,一个消费者组监听多个主题(
topic
),kafka
会根据对象TopicAndPartition
把所有主题的partition
的哈希值进行排序,当作一个对象进行顺序的轮询,每个消费者依次消费消息。 -
Range
(默认策略):范围策略:根据给定的范围值划分消费对应分区的消费者
4.4.3 offset的存储
offset
的作用:由于consumer
在消费过程中可能会出现断电宕机等故障,consumer
恢复后,需要从故障前的位置继续消费,所以consumer
需要实时记录自己消费到那个offset
,以便于故障恢复后继续消费。
Kafka 0.9
版本之前,consumer
默认将offset
保存在Zookeeper
中,从0.9
版本开始,consumer
默认将offset
保存在kafka
一个内置的topic
中,该topic
为_consumer_offsets
1)修改配置文件:consumer.properties
#是否把内部topic的信息(例如offset)暴露给cosumer,如果设置为true,就只能通过订阅的方式来获取内部topic的数据
exclude.internal.topics=false
2)读取offset
#0.11.0.0之前版本
./kafka-console-consumer.sh --topic _consumer_offsets --zookeeper [your.zk.url]:2181 --formatter "kafka.coordinator.GroupMetadaManager\$offsetsMessageFormatter" --consumer.config config/consumer.properties --from-begining
#0.11.0.0之后版本
./kafka-console-consumer.sh --topic _consumer_offsets --zookeeper [your.zk.url]:2181 --formatter "kafka.coordinator.group.GroupMetadaManager\$offsetsMessageFormatter" --consumer.config config/consumer.properties --from-begining
4.4.4 消费者组案例
1)需求:测试同一个消费者组中的消费者,同一时间只能有一个消费者消费
2)案例实操:
- 修改配置文件设置groupId
cd /home/kafka/config
vi consumer.properties
#插入以下内容后保存退出
group.id=testGroup
- 启动消费者组的消费者
#启动消费者,多启动几个
./kafka-xonsole-consumer.sh --zookeeper [your.zk.url]:2181 --topic first --consumer.config config/consumer.properties
./kafka-xonsole-consumer.sh --bootstrap-server [your.kafka.url]:9092 --topic first --consumer.config config/consumer.properties
- 启动生产者
./kafka-console-producer.sh --broker-list [your.kafka.url]:9092 --topic first
- 查看启动的消费者
同一时刻只有一个消费者接收到消息
4.5 高效读写&ZK
的作用
高效读写数据
1)顺序写磁盘
Kafka
的producer
生产数据,要写入到log
文件中,写的过程是一直追加到文件的尾端,为顺序写,数据表明:顺序写优于随机写,因为顺序写节省了大量磁头寻址的时间
2)零复制\零拷贝技术
ZK
的作用
Kafka
集群中有一个broker
会被选举为Controller
,负责管理集群broker
的上下线,所有的topic
的分区副本分配和leader
选举等工作,Controller
的管理工作都是依赖于Zookeeper
的
4.6 事务
Kafka
从0.11
版本开始引入了事务支持,事务可以保证Kafka
在Exactly Once
语义的基础上,生产和消费可以跨分区和会话,要么全部成功,要么全部失败。
Producer事务:
为了实现跨分区跨会话的事务,需要引入一个全局唯一的TransactionId
,并将Producer
获得的PID
和TransactionID
绑定,这样当Producer
重启后就可以通过正在进行的TraansactionId
获得原来的PID
。
为了管理Transaction
,Kafka
引入了一个新的组件TC
,Producer
就是通过和TC
交互获得TransactionID
对应的任务状态,TC
还负责将事务所有写入Kafka
的一个内部Topic
,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。
Consumer事务:
由于Consumer
可以通过offset
访问任意信息,而且不同的SegmentFile
生命周期不同,同一事务的消息可能会出现重启后被删除的情况。