大数据之Kafka(三):Kafka 与 Flume的整合及架构之道

一、Kafka和Flume的整合

1.1 部署实施

Flume主要是做日志数据(离线或实时)的采集。

下图显示的是flume采集完毕数据之后,进行的离线处理和实时处理两条业务线,本文将介绍flume和kafka的整合处理。

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第1张图片

  •  创建整合的topic
# kafka-topics.sh --create \
--topic flume-kafka \
--zookeeper bigdata01:2181/kafka \
--partitions 3 \
--replication-factor 3

Created topic "flume-kafka".
  • 调整Flume-agent配置文件

flume-kafka-sink.conf

##a1就是flume agent的名称
a1.sources = r1
a1.sinks = k1
a1.channels = c1

# Describe/configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 44444

# 修改sink为kafka
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.bootstrap.servers = bigdata01:9092,bigdata02:9092,bigdata03:9092
a1.sinks.k1.kafka.topic = flume-kafka
a1.sinks.k1.kafka.producer.acks = 1
a1.sinks.k1.kafka.producer.linger.ms = 1

# Use a channel which buffers events in memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100

# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
  • 启动Flume和kafka的整合测试
  1. 消费者监听读取的数据
    # kafka-console-consumer.sh --topic flume-kafka \
    --bootstrap-server bigdata01:9092 \
    --from-beginning
  2. 启动flume-agent
    # nohup bin/flume-ng agent -n a1 -c conf -f conf/flume-kafka-sink.conf >/dev/null 2>&1 &
    
  3. 发送数据
    # telnet bigdata01 44444
    Trying 192.168.10.101...
    Connected to bigdata01.
    Escape character is '^]'.
    Hello Oak
    OK
    Good Good Study Day Day Up!
    OK
    
  4. 接收数据
    # kafka-console-consumer.sh --topic flume-kafka --bootstrap-server bigdata01:9092 --from-beginning
    Hello Oak
    Good Good Study Day Day Up!

 二、Kafka架构之道

2.1 Kafka相关术语介绍

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第2张图片

replica:
每⼀个分区,根据副本因子N,会有N个副本。比如在broker1上有一个topic,分区为topic-1, 副本因子为2,那么在两个broker的数据目录里,都会有⼀个topic-1,其中⼀个是leader,⼀个follower。
Segment:
partition 物理上由多个 segment 组成,每个 Segment 存着 message 信息。
Leader:
每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。
Follower:
Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出⼀个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。
Offset
kafka的存储文件都是按照offset.log来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置, 只要找到2048.log的文件即可。当然the first offset就是00000000000.log

2.2 Kafka的架构

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第3张图片

通常,一个典型的Kafka集群中包含若干Producer(可以是web前端产⽣的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干 Consumer Group,以及⼀个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在 Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull 模式从broker订阅并消费消息。

2.3 Kafka的分布式模型

Kafka分布式主要是指分区被分布在多台server(broker)上,同时每个分区都有leader和follower(不是必须),即老大和小弟的角色,老大负责处理,小弟负责同步,小弟也可以变成老大,形成分布式模型。

kafka的分区日志(message)被分布在kafka集群的服务器上,每⼀个服务器处理数据和共享分区请求。每⼀个分区是被复制到⼀系列配置好的服务器上来进行容错。

每个分区有⼀个server节点来作为分区leader和零个或者多个server节点来作为分区followers。分区leader处理指定分区的所有读写请求,同时分区follower被动复制分区leader。如果leader失败,follwers中的⼀个将会自动地变成⼀个新的leader。每⼀个服务器都能作为分区的⼀个leader和作为其它分区的follower,因此kafka集群能被很好地平衡。kafka集群是一个去中心化的集群。

以上信息参考官网:http://kafka.apache.org/intro.html#intro_distribution

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第4张图片

kafka消费的并行度就是kafka topic分区的个数,或者说分区的个数决定了同一时间同一消费者组内最多可以有多少个消费者消费数据。 

2.4 kafka的文件存储

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第5张图片

  • 在kafka集群中,分为单个broker和多个broker。每个broker中有多个topic,topic数量可以自己设定。在每个topic中又有0到多个partition,每个partition为一个分区。kafka分区命名规则为topic的名称+有序序号,这个序号从0开始依次增加。
  • 每个partition中有多个segment file。创建分区时,默认会生成一个segment file,kafka默认每个segmentfile的大小是1G。当生产者向partition中存储数据时,内存中存不下了,就会往segment file里面刷新。在存储数据时,会先生成⼀个segment file,当这个segment file到1G之后,再生成第⼆个segment file 以此类推。每个segment file对应两个⽂件,分别是以.log结尾的数据文件和以.index结尾的索引文件。在服务器上,每个partition是一个目录,每个segment是分区目录下的一个文件。
  • 每个segment file也有自己的命名规则,每个名字有20个字符,不够用0填充。每个名字从0开始命名,下⼀个segment file文件的名字就是,上⼀个segment file中最后⼀条消息的索引值。在.index文件中,存储的是key-value格式的,key代表在.log中按顺序开始第n条消息,value代表该消息的位置偏移。但是在.index中不是对每条消息都做记录,它是每隔⼀些消息记录⼀次,避免占用太多内存。即使消息不在index记录中,在已有的记录中查找,范围也大大缩小了。
    .index中存放的消息索引是⼀个稀疏索引列表。

2.5 topic中的partition

分区的目的

可以想象,如果⼀个topic就⼀个分区,要是这个分区有1T数据,那么kafka就想把大文件划分到更多的目录来管理,这就是kafka所谓的分区。

分区的好处

  • 方便在集群中扩展。因为一个topic由⼀个或者多个partition构成,而每个节点中通常可以存储多个 partition,这样就方便分区存储与移动,也就增加其扩展性。同时也可以增加其topic的数据量。
  • 可以提高并发。因为一个主题多个partition,而每个主题读写数据时,其实就是读写不同的partition,所以增加了其并发性。

单节点partition的存储分布

Kafka集群只有⼀个broker,默认/var/log/kafka-log为数据文件存储根目录,在Kafka broker中 server.properties文件配置(参数log.dirs=/opt/data/kafka),例如创建2个topic名称分别为test-1、test-2, partitions数量都为partitions=4

存储路径和目录规则为:

 |--test-1-0 
 |--test-1-1
 |--test-1-2
 |--test-1-3
 |--test-2-0
 |--test-2-1
 |--test-2-2
 |--test-2-3

在Kafka文件存储中,同⼀个topic下有多个不同partition,每个partition为⼀个⽬录,partiton命名规则为:topic 名称+分区编号(有序),第⼀个partiton序号从0开始,序号最⼤值为partitions数量减1。

多节点partition存储分布

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第6张图片

 2.5.1 分区分配策略

  1. 将所有broker(n个)和partition排序
  2. 将第i个Partition分配到(i mod n)个broker上

分区策略举例

test3的topic,4个分区,2个副本。

# kafka-topics.sh --describe --zookeeper bigdata01:2181/kafka --topic test3
Topic:test3 PartitionCount:4 ReplicationFactor:2 Configs:
 Topic: test3 Partition: 0 Leader: 1 Replicas: 1,3 Isr: 1,3
 Topic: test3 Partition: 1 Leader: 2 Replicas: 2,1 Isr: 1,2
 Topic: test3 Partition: 2 Leader: 3 Replicas: 3,2 Isr: 2,3
 Topic: test3 Partition: 3 Leader: 1 Replicas: 1,2 Isr: 1,2

第1个Partition分配到第(1 mode 3)= 1个broker上
第2个Partition分配到第(2 mode 3)= 2个broker上
第3个Partition分配到第(3 mode 3)= 3个broker上
第4个Partition分配到第(4 mode 3)= 1个broker上

 2.5.2 副本分配策略

  • 在Kafka集群中,每个Broker都有均等分配Partition的Leader机会。
  • 上述图Broker Partition中,箭头指向为副本,以Partition-0为例:broker3中parition-0为Leader,Broker1中Partition-0为副本。
  • 上述图中每个Broker(按照BrokerId有序)依次分配主Partition,下⼀个Broker为副本,如此循环迭代分配,多副本都遵循此规则。

副本分配算法:

  • 将所有N Broker和待分配的i个Partition排序
  • 将第i个Partition分配到第(i mod n)个Broker上
  • 将第i个Partition的第j个副本分配到第((i + j)mod n)个Broker上。(j > = 1)

分区及副本分配举例

# kafka-server-start.sh config/server.properties   #0 0
# kafka-server-start.sh config/server.1.properties #10 1
# kafka-server-start.sh config/server.2.properties #20 2
zookeeper-shell.sh localhost:2181
ls /brokers/ids
[0, 20, 10]
# kafka-topics.sh --bootstrap-server hadoop00:9093 --create --topic test1 --partitions 3 --replication-factor 2
# kafka-topics.sh --bootstrap-server hadoop00:9093 --describe --topic test1
Topic:test1 PartitionCount:3 ReplicationFactor:2 Configs:segment.bytes=1073741824
     Topic: test1 Partition: 0 Leader: 20 Replicas: 20,10 Isr: 20,10
     Topic: test1 Partition: 1 Leader: 0 Replicas: 0,20 Isr: 0,20
     Topic: test1 Partition: 2 Leader: 10 Replicas: 10,0 Isr: 10,0
 
 
     分区0 第1个副本应该位于 0+1%3=1 =>broker[1]= 10
     分区0 第2个副本应该位于 0+2%3=2 =>broker[2]= 20
 
     分区1 第1个副本应该位于 1+1%3=2 =>broker[2]= 20
     分区1 第2个副本应该位于 1+2%3=0 =>broker[0]= 0

2.5.3 数据分配策略

  • 如果指定了partition,进入该partition
  • 如果没有指定partition,但是指定了key,通过key的字节数组信息的hashcode值和partition数求模确定partition
  • 如果都没有指定,通过轮询方式进入对应的partition

 2.6 partition中文件存储

下图是⼀个partition-0的存储示意图

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第7张图片

  • 每个分区⼀个⽬录,该⽬录中是⼀堆segment file(默认⼀个segment是1G),该目录和file都是物理存储于磁盘。
  • 每个partion(目录)相当于⼀个巨型文件被平均分配到多个大小相等segment(段)数据⽂件中。但每个段segment file消息数量不⼀定相等,这种特性⽅便old segment file快速被删除。
  • 每个partiton只需支持顺序读写即可,segment文件生命周期由服务端配置参数决定。
  • 这样做的好处就是能快速删除无用文件,有效提高磁盘利用率。

2.7 kafka分区中的segemnt (重点)

Kafka文件系统是以partition方式存储,下面深入分析partitiion中segment file组成和物理结构。

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第8张图片

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第9张图片通过上面两张图,我们已经知道topic、partition、segment、.log、.index等文件的关系,下⾯深⼊介绍segment 相关组成原理。

segment file组成:

由2大部分组成,分别为index file和log file(即数据文件),这2个⽂件⼀⼀对应,成对出现,后缀".index"和“.log” 分别表示为segment索引文件、数据文件。

segment⽂件命名规则:

partition全局的第⼀个segment从0开始,后续每个segment⽂件名为上⼀个segment⽂件最后⼀条消息的 offset值。数值最大为64位long大小,20位数字字符长度,不够的左边用0填充。

验证:

创建⼀个topic为test5包含1 partition,设置每个segment⼤⼩为1G,并启动producer向Kafka broker写入大量数据。

# bin/kafka-topics.sh --create --zookeeper bigdata01:2181/kafka --replication-factor 1 --partitions 1 --topic test5
Created topic "test5".

# ./bin/kafka-console-producer.sh --broker-list bigdata01:9092 --topic test5
>nihao beijing
>nihao qianfeng
>124
>123456789
>098765
>999
>laowang
......

查看segment文件列表:

# ll /opt/data/kafka/test5-0/ #查看分区⽬录
total 8
-rw-r--r--. 1 root root 10485760 Nov 21 10:50 00000000000000000000.index #segment⽂件索引⽂件
-rw-r--r--. 1 root root 1073761826 Nov 21 10:53 00000000000000000000.log #segment的log⽂件
-rw-r--r--. 1 root root 10485760 Nov 21 10:50 00000000000000023060.index
-rw-r--r--. 1 root root 892 Nov 21 10:53 00000000000000023060.log
-rw-r--r--. 1 root root 10485756 Nov 21 10:50 00000000000000003268.timeindex
-rw-r--r--. 1 root root 8 Nov 21 10:52 leader-epoch-checkpoint

查看segment文件内容

1. 查看.log

# usr/local/kafka/bin/kafka-run-class.sh kafka.tools.DumpLogSegments \
--files 00000000000000000000.log --print-data-log

效果:

Starting offset: 0
offset: 0 position: 0 CreateTime: 1577994283622 isvalid: true keysize: -1 valuesize: 1 magic: 2
compresscodec: NONE producerId: -1 producerEpoch: -1 sequence: -1 isTransactional: false
headerKeys: [] payload: a

offset: 1 position: 69 CreateTime: 1577994466159 isvalid: true keysize: -1 valuesize: 1 magic: 2
compresscodec: NONE producerId: -1 producerEpoch: -1 sequence: -1 isTransactional: false
headerKeys: [] payload: 1

offset: 2 position: 138 CreateTime: 1577994474463 isvalid: true keysize: -1 valuesize: 1 magic: 2
compresscodec: NONE producerId: -1 producerEpoch: -1 sequence: -1 isTransactional: false
headerKeys: [] payload: 4

2. 查看.index

# /usr/local/kafka/bin/kafka-run-class.sh kafka.tools.DumpLogSegments \
--files 00000000000000000000.index --print-data-log

Dumping 00000000000000000000.index
offset: 0 position: 0

segment⽂件的物理结构:

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第10张图片 .index和.log物理结构对应关系

如上图所示,索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。

 举例

上述索引⽂件中元数据6-->266为例,依次在数据⽂件中表示第6个message(在全局partiton表示第20366个 message)、以及该消息的物理偏移地址为266。

message物理结构

⼀个segment data file由许多message组成,⼀个message物理结构具体如下:

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第11张图片 message物理结构

 具体参数详解:

关键字 解释说明
8 byte offset 在parition(分区)内的每条消息都有⼀个有序的id号,这个id号被称为偏移(offset),它可以唯⼀ 确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message
4 byte message size message大小
4 byte CRC32 用crc32校验message
1 byte “magic" 表示本次发布Kafka服务程序协议版本号
1 byte “attributes" 表示为独⽴版本、或标识压缩类型、或编码类型
4 byte key length 表示key的⻓度,当key为-1时,K byte key字段不填
K byte key 可选
value bytes payload 表示实际消息数据

2.8 kafka中消息查找流程

举例

查找offset=23066的message,需要通过如下2个步骤查找:

第一步 查找segment file

00000000000000000000.index
00000000000000000000.log
00000000000000023060.index
00000000000000023060.log

根据.index和.log物理结构对应关系图可知,其中00000000000000000000.index表示最开始的⽂件,起始偏移 量(offset)为0.第⼆个⽂件00000000000000023060.index的消息量起始偏移量为23060 = 23059 + 1.同样,其他后 续⽂件依次类推,以起始偏移量命名并排序这些⽂件,只要根据offset ⼆分查找⽂件列表,就可以快速定位到具体文件。

当offset=23066时定位到0000000000000023060.index和log⽂件。

第⼆步 通过segment file查找message

通过第⼀步定位到segment file,当offset=23066时,依次定位到0000000000000023060.index的元数据物理 位置和 0000000000000023060.log的物理偏移地址,然后再通过0000000000000023060.log顺序查找直到 offset=23066为⽌。

segment index file采取稀疏索引存储方式,即<偏移量、位置>,它减少索引文件大小,通过map可以直接内存 操作,稀疏索引为数据⽂件的每个对应message设置⼀个元数据指针,它⽐稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。

2.9 Consumer Group架构

consumer group是kafka提供的可扩展且具有容错性的消费者机制。既然是⼀个组,那么组内必然可以有多个消费 者或消费者实例(consumer instance),它们共享⼀个公共的ID,即group ID。组内的所有消费者协调在⼀起来消 费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同⼀个消费组内的⼀个consumer来 消费。理解consumer group记住下⾯这三个特性就好了:

  • consumer group下可以有⼀个或多个consumer instance,consumer instance可以是⼀个进程,也可以是⼀个线程
  • group.id是⼀个字符串,唯⼀标识⼀个consumer group
  • consumer group下订阅的topic下的每个分区只能分配给某个group下的⼀个consumer(当然该分区还可以被分配给其他group)

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第12张图片

 2.10 offset的维护

由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消 费,所以consumer需要实时记录⾃⼰消费到了哪个offset,以便故障恢复后继续消费。

Kafka默认是定期帮你⾃动提交位移的(enable.auto.commit = true),你当然可以选择⼿动提交位移实现⾃⼰控 制。另外kafka会定期把group消费情况保存起来,做成⼀个offset map,如下图所示:

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第13张图片

上图表示,有⼀个test-group的消费者组,该组消费两个分区分别为topicA-0和topicA-1,对topicA-0分区消费到 offset为8这个位置,⽽对topicA-1分区消费到offset为6这个位置。

Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,zk中的目录结构是:/consumers/[group.id] (http://group.id/)/offsets//。但是zookeeper其实并不适合进行大批量的读写操作,尤其是写操作。 因此从0.9版 本开始,consumer默认将offset保存在Kafka⼀个内置的topic中,该topic为__consumer_offsets。该topic的格式大概如下:

group.id:分组id,唯⼀。

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第14张图片

high level 和low level

  • 将zookeeper维护offset 的⽅式称为 low level API
  • 将kafka broker 维护offset的⽅式称为high level API

使⽤high level API 更新offset具体设置
可以在consumer的代码中设置这个属性

  • 自动提交,设置enable.auto.commit=true,更新的频率根据参数【auto.commit.interval.ms】来定。这种方式也被称为【at most once】,fetch到消息后就可以更新offset,无论是否消费成功。默认就是true。
  • 手动提交,设置enable.auto.commit=false,这种⽅式称为【at least once】。fetch到消息后,等消费完成再调用方法【consumer.commitSync()】,手动更新offset;如果消费失败,则offset也不会更新,此条消息会被重复消费⼀次。

2.11 Kafka中push和pull 

  • ⼀个较早问题是我们应该考虑是消费者从broker中pull数据还是broker将数据push给消费者。kakfa遵守传统设计和借鉴很多消息系统,这⼉kafka选择producer向broker去push消息,并由consumer从broker pull消息。⼀些ogging-centric system,⽐如Facebook的Scribe和Cloudera的Flume,采⽤⾮常不同的push模式。事实上,push模式和pull模式各有优劣。
  • push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。push模式的⽬标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及⽹络拥塞。⽽pull模式则可以根据consumer的消费能⼒以适当的速率消费消息。
  • pull模式不足之处是,如果kafka没有数据,消费者可能会陷⼊循环中,⼀直返回空数据。针对这⼀点,Kafka的消费者在消费数据时会传⼊⼀个时⻓参数timeout,如果当前没有数据可供消费,consumer会等待⼀段时间之后再返回,这段时⻓即为timeout。
  • ps:timeout官⽅案例是100毫秒

2.12 kafka中数据发送保障

        为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后, 都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进⾏下⼀轮的发送,否则重新发送数据。

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第15张图片

 1)副本数据同步策略

⽅案 优点 缺点
半数以上完成同步,就发送ack 延迟低 选举新的leader时,容忍n台节点的故障,需要2n+1个副本
全部完成同步,才发送ack 选举新的leader时,容忍n台节点的故障,需要n+1个副本 延迟⾼

Kafka选择了第⼆种⽅案,原因如下:

  1. 同样为了容忍n台节点的故障,第⼀种方案需要2n+1个副本,而第⼆种⽅案只需要n+1个副本,而Kafka的每个分 区都有⼤量的数据,第⼀种方案会造成大量数据的冗余。
  2. 虽然第二种⽅案的网络延迟会比较⾼,但网络延迟对Kafka的影响较小。

2)ISR

采⽤第⼆种⽅案之后,设想以下情景:leader收到数据,所有follower都开始同步数据,但有⼀个follower,因为某种故障,迟迟不能与leader进⾏同步,那leader就要⼀直等下去,直到它完成同步,才能发送ack。这个问题怎 么解决呢?

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。

注:

  • ⽣产者发送到特定主题分区的消息是将按照发送的顺序来追加。也就是说,如果消息M1和消息M2由相同的⽣产者发送,并且M1是先发送的,那么M1的偏移量将⽐M2低,并出现在⽇志的前⾯。
  • 消费者是按照存储在⽇志中记录顺序来查询消息。
  • 对于具有n个副本的主题,我们将容忍最多N-1个服务器失败故障,从⽽不会丢失提交到⽇志的任何消息记录。

 2.13 ack应答机制

对于某些不太重要的数据,对数据的可靠性要求不是很⾼,能够容忍数据的少量丢失,所以没必要等ISR中的 follower全部接收成功。

所以Kafka为⽤户提供了三种可靠性级别,⽤户根据对可靠性和延迟的要求进⾏权衡,选择以下的配置。 

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第16张图片

 故障处理细节

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第17张图片

 (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同步数据。

注意:这只能保证副本之间的数据⼀致性,并不能保证数据不丢失或者不重复。

2.14 Exactly Once语义

对于某些⽐较重要的消息,我们需要保证exactly once(⼀次正好)语义,即保证每条消息被发送且仅被发送⼀次。

在0.11版本之后,Kafka引入了幂等性机制(idempotent),配合acks = -1时的at least once(最少⼀次)语义, 实现了producer到broker的exactly once语义。

*idempotent + at least once = exactly once*

使⽤时,只需将enable.idempotence属性设置为true(在⽣产者的位置),kafka⾃动将acks属性设为-1。

ps:幂等性机制是什么意思,幂等简单说1的⼏次幂都等于1,也就是说⼀条消息⽆论发⼏次都只算⼀次,⽆论多少条消 息但只实例化⼀次

kafka完成幂等性其实就是给消息添加了唯⼀ID, 这个ID的组成是PID(ProducerID)这样保证每⼀个Producer发送 的时候是唯⼀的,还会为Producer中每条消息添加⼀个消息ID,也就是说当前Producer中⽣产的消息会加⼊ Producer的ID和消息ID这样就能保证消息唯⼀了,这个消息发送到Kafka中的时候回暂时缓存ID,写⼊数据后没有收 到ack,那么会从新发送这个消息,新消息过来的时候会和缓存中ID进⾏⽐较如果发现已经存在就不会再次接受了

详细解析:

为了实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。

PID。每个新的Producer在初始化的时候会被分配⼀个唯⼀的PID,这个PID对⽤户是不可⻅的。

Sequence Numbler。(对于每个PID,该Producer发送数据的每个都对应⼀个从0开始单调 递增的Sequence Number

Kafka可能存在多个⽣产者,会同时产⽣消息,但对Kafka来说,只需要保证每个⽣产者内部的消息幂等就可以了,所以引入了PID来标识不同的⽣产者。

对于Kafka来说,要解决的是⽣产者发送消息的幂等问题。也即需要区分每条消息是否重复。 Kafka通过为每条消息增加⼀个Sequence Numbler,通过Sequence Numbler来区分每条消息。每条消息对应⼀个分区,不同的分区产⽣的消息不可能重复。所有Sequence Numbler对应每个分区

Broker端在缓存中保存了这seq number,对于接收的每条消息,如果其序号⽐Broker缓存中序号⼤1则接受 它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同⼀个的Exactly Once语义。不能保证同⼀个Producer⼀个topic不同的partion幂等。

2.15 Zookeeper在Kafka中的作用

Kafka集群中有⼀个broker会被选举为Controller,负责管理集群broker的上下线,所有topic的分区副本分配和 leader选举等⼯作。

Controller的管理⼯作都是依赖于Zookeeper的。

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第18张图片

 只有KafkaController Leader会向zookeeper上注册Watcher,其他broker⼏乎不⽤监听zookeeper的状态变化。

Kafka集群中多个broker,有⼀个会被选举为controller leader(谁先到就是谁),负责管理整个集群中分区和副本 的状态,⽐如partition的leader 副本故障,由controller 负责为该partition重新选举新的leader 副本;当检测到 ISR列表发⽣变化,有controller通知集群中所有broker更新其MetadataCache信息;或者增加某个topic分区的时 候也会由controller管理分区的重新分配⼯作

当broker启动的时候,都会创建KafkaController对象,但是集群中只能有⼀个leader对外提供服务,这些每个 节点上的KafkaController会在指定的zookeeper路径下创建临时节点,只有第⼀个成功创建的节点的 KafkaController才可以成为leader,其余的都是follower。当leader故障后,所有的follower会收到通知,再次竞 争在该路径下创建节点从⽽选举新的leader。

三、Kafka的log

大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第19张图片

 3.1 kafka的log的写

日志允许序列附加,总是附加到最后⼀个⽂件。当该⽂件达到可配置的⼤⼩(⽐如1GB)时,就会将其刷新到⼀个新⽂件。⽇志采⽤两个配置参数:M和S,前者给出在强制OS将⽂件刷新到磁盘之前要写⼊的消息数量(条数),后者给出多少秒之后被强制刷新。这提供了⼀个持久性保证,在系统崩溃的情况下最多丢失M条消息或S秒的数据。

3.2 kafka的log读

1、读取的实际过程是:⾸先根据offset去定位数据⽂件中的log segment⽂件,然后从全局的offset值中计算指定⽂件offset,然后从指定⽂件offset读取消息。查找使⽤的是⼆分查找(基于快排队segment⽂件名进⾏排序),每⼀个⽂件的范围都被维护到内存中。
2、读取是通过提供消息的64位逻辑偏移量(8字节的offset)和s字节的最⼤块⼤⼩来完成。
3、读取将返回⼀个迭代器包含有s字节的缓冲区,缓冲区中含有消息。S字节应该⽐任何单个消息都⼤,但是在出现异常⼤的消息时,可以多次重试读取,每次都将缓冲区⼤⼩加倍,直到成功读取消息为⽌。
4、可以指定最⼤的消息(message.max.bytes)和缓冲区大小,以使服务器拒绝的消息大于某个大小,并为客户机提供其获得完整消息所需的最⼤读取量。

3.3 kafka的log的删除

  1. 删除log segment即数据被删除。
  2. ⽇志管理器允许可插拔删除策略去选择那些最符合删除的⽂件来删除。
  3. 当前删除策略是修改时间超过N天以前的数据(log.retention.hours),但保留最近NGB的策略也很有⽤(log.retention.bytes)。

3.4 kafka的log的保障

  1. 日志提供了⼀个配置参数M,该参数控制在强制刷新磁盘之前写⼊的消息的最⼤数量(M条)。
     

    大数据之Kafka(三):Kafka 与 Flume的整合及架构之道_第20张图片

  2. 启动日志恢复去处理最近消息在总消息中是否有效,使⽤crc32来校验,如果消息⻓度和offset总和小于文件长度且crc32和存储的消息能匹配上,则表示有效。

注意: 必须处理两种类型的损坏:中断(由于崩溃⽽丢失未写的块)和损坏(向⽂件添加⽆意义块)。

你可能感兴趣的:(#,Kafka,kafka)