4 Kafka架构原理
4.1 Kafka工作流程及文件存储机制
Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。
- topic是逻辑上的概念,partition是物理上的概念。每个partition对应一个log文件,log文件存放的是producer生产的数据。producer生产的数据会不断的追加到log文件末端,而且每一条数据都有自己的偏移量offset。
- 消费者组中的消费者,都会实时记录自己消费到哪个offset,以便出错恢复时,从上次位置继续消费。
因为log文件一直被追加,为了防止log存放数据的文件太大,查找效率太低。
Kafka采用①分片:log文件每1G就分一个片
②索引机制:index文件
00000000000000.log存放大量的数据,00000000000000.index存放大量的索引信息。
4.2 Kafka生产者
①分区策略
Kafka利用topic进行分类,然后每一个topic又进行分区操作。
分区的原则:将producer发送的数据封装成一个ProducerRecord对象(分3种情况)
(1)指明partition,直接将指明的值作为partition值。
(2)没有指明partition值,但有key,将key的hash值与topic的partition数进行取余得到partition的值。
(3)既没有partition的值有没有key,kafka采用Sticky Partition(黏性分区器),会随机选择一个分区,并尽可能一直使用该分区,待该分区的batch已满或者已完成,kafka再随机一个分区进行使用。
②数据可靠性保证
生产者发送数据到topic partition的数据可靠性保证:
ACK应答策略
producer产生的消息发送给每个topic的partition(其实只发给了leader),每个partition收到消息后会发送一个ack(acknowledgement确认收到)
- 如果producer收到了ack,就进行下一轮发送(!!!kafka是异步发送,不用收到ack就可以直接发送)
- 如果producer没有收到ack,就重新发送
ack应答级别
默认ack=1
0 : 最低的延迟,leader接收到还没写入磁盘就返回ack;leader故障时可能丢数据
1 : leader写完后返回ack,不等待follower写完;follower同步成功之前leader故障肯能会丢数据
-1 : leader和ISO内的所有的follower写完后,才返回ack;broker返回给producer时,leader故障,可能会数据重复
ISR同步副本策略
Kafka采用了ISR同步副本的策略进行同步副本。
Leader维护了一个动态的ISR(in-sync replica set),只要follower和leader的通信时间够快,就能进入到ISR集合中。时间阈值由replica.lag.time.max.ms参数设定。只同步ISR内所有的follower。
谁可以进入到ISR这个集合?之前的版本中:
- 通信时间够快(follower和leader交互快)默认10s
- 消息条数,follower和leader同步的数据差距小的。(差距在10条内的进来,大于10条剔除)
新版本0.9取消了消息的条数,为什么去除呢?
如果一次batch发送了11条给leader,此时全部的follower全部超过10条,全部剔除。同步很快,很快又小于10条,又全部拉回了ISR集合,这个发送频繁后,不仅消耗资源还增大ZK压力。
leader和follower故障处理
LEO:指的是每个副本最大的offset;
HW:指的是消费者能见到的最大的offset,ISR队列中最小的LEO。
(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同步数据。
HW
消费者最大可见LEO=HW,作用(1)保证了消费者消费数据的一致性。
参差不齐的LEO,选出的leader会通知其他的follower,比leader高就截去,低就同步。作用(2)保证了数据存储的一致性。
只能保证数据的一致性,但是不能保证是否有数据丢失或重复(ack应答级别的事)
③Exactly One语义
-
At Least Once语义:ACK级别设置为-1
-
At Most Once语义:ACK级别设置为0
-
Exactly Once:At Least Once + 幂等性
at least once的ack为-1,leader和ISR内的所有follower都同步完,再发送ack,但是broker发送ack给producer的时候,如果leader出错误了,producer会重发。这个时候回出现重复数据。
at most once的ack为0,不管leader和follower是否同步完,就返回ack,这个时候leader挂掉,会丢失数据。
在at least once的基础上引入幂等性,在producer发送数据时,就算重发了数据,也会只持久化一条相同数据。
开启幂等性是在Producer初始化的时候分配一个PID,发往Partition的消息会附带一个Sequence Number。而在Broker端会对<PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时,Broker只会持久化一条。
但是PID重启就会变化,同时不同的Partition也具有不同的主键,所以幂等性不能跨分区、不能跨会话。
# 开启幂等性
enable.idempotence=true
4.3 Kafka消费者
kafka消费者采用pull拉取的方式从broder读取数据。
push(推模式)缺点:很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。
pull模式缺点:如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据。
①分区分配策略
有两种:RoundRobin和Range两种策略。
RoundRobin策略
根据组去划分。将某一个Topic的不同分区,封装成多个对象,通过比较对象的hash值排序,轮询的方式,分给不同的消费者。
Range策略(系统默认)
根据主题划分。将某一个Topic的不同分区,根据分区数/消费者数,将余数给第一个消费者,模数给其他的消费者。
消费者组订阅不同的Topic呢?
- RoundRobin:如果一个消费者组内的不同消费者订阅了不同的Topic,还按照轮询的方式分配给不同的消费者,会出现A订阅了B的Topic,这样就乱了!!!
- 所以如果RoundRobin策略下的消费者组内的不同消费者向订阅多个Tiopic,就必须组内的所有消费者订阅相同的多个Topic!!!
- Range:Range策略下,就能够解决上面的订阅乱了的现象!
② offset的维护
因为消费者在消费的过程中,有可能出现断电宕机等故障,consumer恢复后,应该从故障前的位置继续消费,所以consumer需要实时记录自己消费到哪个offset。
-
在0.9版本之前,consumer默认将offset保存在zookeeper中,从0.9版本开始consumer默认将offset保存在Kafka一个内置的topic中,该topic为__consumer_offsets
-
本地中:按照GTP(groupid,topic,partition)就能确定唯一一个offset
思想: __consumer_offsets 为kafka中的topic, 那就可以通过消费者进行消费
那么当前这个消费者组消费的信息在哪个分区保存着呢?
系统50个分区根据GTP的hash值取选择存到这50个分区中的某一个中。
步骤1:修改consumer.properties
可以让普通的消费者消费系统的topic,不改为false的话,系统的topic无法消费。
# 不排除内部的topic
exclude.internal.topics=false
步骤2:创建一个topic
[atguigu@hadoop102 config]$ kafka-topics.sh --zookeeper hadoop102:2181 --create --topic WEI --partitions 2 --replication-factor 2
步骤3:启动生产者和消费者,分别向WEI这个topic生产数据和消费数据
[atguigu@hadoop102 ~]$ kafka-console-producer.sh --broker-list hadoop102:9092 --topic WEI
[atguigu@hadoop102 ~]$ kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic WEI --from-beginning
步骤4:消费系统的topic(offset)
[atguigu@hadoop102 kafka]$ kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic __consumer_offsets --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --consumer.config config/consumer.properties --from-beginning
步骤5:收到的数据
[test-consumer-group,atguigu,1]::OffsetAndMetadata(offset=2, leaderEpoch=Optional[0],
metadata=, commitTimestamp=1591935656078, expireTimestamp=None)
[test-consumer-group,atguigu,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional[0], metadata=, commitTimestamp=1591935656078, expireTimestamp=None)
③消费者组案例
测试同一个消费者组中的消费者,同一时刻只能有一个消费者消费
步骤1:在hadoop102,hadoop103上修改/opt/module/kafka/config/consumer.properties配置文件中的group.id属性为任意组名。
[atguigu@hadoop102 config]$ vim consumer.properties
group.id=mygroup
步骤2:在hadoop104上启动生产者
[atguigu@hadoop104 kafka]$ bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic first
步骤3:在hadoop102、hadoop103上分别启动消费者
因为配置了consumer.properties内的groupid,所以需要指定当前的消费者是哪个组的。如果不指定,会随机生成一个id,也就是自己是一个组。
[atguigu@hadoop102 kafka]$ bin/kafka-console-consumer.sh bootstrap-server hadoop102:9092 --topic first --consumer.config config/consumer.properties
[atguigu@hadoop103 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic first --consumer.config config/consumer.properties
步骤4:查看hadoop102和hadoop103的消费者的消费情况。
4.4 Kafka高效读写数据
①顺序写磁盘
②应用Pagecache
③零复制技术
直接由操作系统操作拷贝文件。
**④分布式(分区)**并发读写
4.5 Zookeeper在Kafka中的作用
①元数据存储
zookeeper在kafka中的第一个作用就是,元数据的存储。
- Kafka的brokers信息,包含了集群id、topic等信息。
- cluster集群元数据信息。
- config配置文件信息。
- controller集群的老大,一般是broker0。
- 0.9版本的consumer的offset等信息也保存在zookeeper。如:/consumers/ [消费者组A,…]现在没有
②Controller管理
zookeeper的第二个作用就是,为controller管理broker,所有分区副本的分配和leader选举提供支持。
- Controller的选举机制是竞争资源,哪个broker抢到资源谁就是Controller。Controller负责管理集群broker的上下线,分区副本分配、leader的选举。
- leader是怎样选举的呢?
第一次选leader是随机的,之后如果leader挂掉了,那么此后是轮询的方式,选择下一个作为leader。
4.6 Kafka事务
0.11版本引入事务。
- kafka的幂等性生产和消费不能够跨分区和会话;引入kafka事务后在Exactly Once语义的基础上,生产和消费可以跨分区和会话,要么全部成功要么全部失败。
①Producer事务
Producer事务保证了精准一次性写入
因为幂等性只能在当前会话和当前分区有效,所以需要一个全局唯一的Transaction ID,并将Producer获得的PID和Transaction ID绑定。这样当Producer重启后就可以通过正在进行的Transaction ID找到原来的PID。
为了管理Transaction,kafka引入一个新的组件Transaction Coordinator 。Producer就是通过和Transaction Coordinator交互获得Transaction ID对应的任务状态。Transaction Coordinator还负责将事务所有写入Kafka的一个内部Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。
②Consumer事务
Consumer事务解决的是精准一次性消费的问题。
消费者消费了消息,但是没有将offset更新,这个时候消费者挂掉了,再次消费的时候就会重复消费。