Kafka 入门

一、简介

Kafka 是最初由 Linkedin 公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于 zookeeper 协调和发布/订阅模式的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于 hadoop 的批处理系统、低延迟的实时系统、storm/Spark 流式处理引擎,web/nginx 日志、访问日志,消息服务等等,用 scala 语言编写,Linkedin 于2010年贡献给了 Apache 基金会并成为顶级开源项目。

1、Kafka 的特性

kafka 是一个分布式的基于发布订阅模式的消息队列,主要应用于大数据实时处理领域。(kafka 是消费者主动拉取生产者的信息

1)高吞吐量、低延迟:kafka 每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个 topic 可以分多个 partition,consumer group 对 partition 进行 consume 操作。

2)可扩展性:kafka 集群支持热扩展。

3)持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失

4)容错性:允许集群中节点失败(若副本数量为n,则允许 n-1 个节点失败)。

5)高并发:支持数千个客户端同时读写。

2、Kafka 的使用场景

1)日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。

2)消息系统:解耦和生产者和消费者、缓存消息等。

3)用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。

4)运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。

5)流式处理:比如spark streaming和storm

6)事件源

3、Kafka 的消息

(1)message 状态

在 Kafka 中,消息的状态被保存在 consumer 中,broker 不会关心哪个消息被消费了被谁消费了,只记录一个offset值(指向partition中下一个要被消费的消息位置),这就意味着如果 consumer 处理不好的话,broker 上的一个消息可能会被消费多次。

(2)message 持久化

Kafka 中会把消息持久化到本地文件系统中,并且保持极高的效率。我们众所周知 IO 读取是非常耗资源的性能也是最慢的,这就是为了数据库的瓶颈经常在 IO 上,需要换 SSD 硬盘的原因。但是 Kafka 作为吞吐量极高的 MQ,却可以非常高效的 message 持久化到文件。这是因为 Kafka 是顺序写入o(1)的时间复杂度,速度非常快。也是高吞吐量的原因。由于 message 的写入持久化是顺序写入的,因此 message 在被消费的时候也是按顺序被消费的。一般的机器,单机每秒100k条数据。

(3)message 有效期

Kafka 会长久保留其中的消息,以便 consumer 可以多次消费,当然其中很多细节是可配置的。

4、负载均衡

kafka 集群中的任何一个 broker,都可以向 producer 提供 metadata 信息,这些 metadata 中包含"集群中存活的 servers 列表"/“partitions leader 列表"等信息(请参看zookeeper中的节点信息)。当 producer 获取到 metadata 信息之后,producer 将会和 Topic 下所有 partition leader 保持 socket 连接,消息由 producer 直接通过 socket 发送到 broker,中间不会经过任何"路由层”。

异步发送,将多条消息暂且在客户端 buffer 起来,并将他们批量发送到 broker;小数据IO太多,会拖慢整体的网络延迟,批量延迟发送事实上提升了网络效率;不过这也有一定的隐患,比如当 producer 失效时,那些尚未发送的消息将会丢失。

5、log

每个 log entry 格式为"4个字节的数字N表示消息的长度" + “N个字节的消息内容”,每个日志都有一个 offset 来唯一的标记一条消息,offset 的值为8个字节的数字,表示此消息在此 partition 中所处的起始位置。每个 partition 在物理存储层面,由多个 log file 组成(称为segment)。segment file 的命名为"最小 offset".kafka,例如"00000000000.kafka",其中"最小 offset"表示此 segment 中起始消息的 offset。

获取消息时,需要指定 offset 和最大 chunk 尺寸,offset 用来表示消息的起始位置,chunk size 用来表示最大获取消息的总长度(间接的表示消息的条数)。根据 offset,可以找到此消息所在 segment 文件,然后根据 segment 的最小 offset 取差值,得到它在 file 中的相对位置。直接读取输出即可。
在这里插入图片描述

6、分布式

kafka 使用 zookeeper 来存储一些 meta 信息,并使用了 zookeeper watch 机制来发现 meta 信息的变更并作出相应的动作(比如 consumer 失效,触发负载均衡等)。

1)Broker node registry:当一个 kafka broker 启动后,首先会向 zookeeper 注册自己的节点信息(临时znode),同时当 broker 和 zookeeper 断开连接时,此 znode 也会被删除。

2) Broker Topic Registry:当一个 broker 启动时,会向 zookeeper 注册自己持有的 topic 和 partitions 信息,仍然是一个临时znode。

3) Consumer and Consumer group:每个 consumer 客户端被创建时,会向 zookeeper 注册自己的信息,此作用主要是为了"负载均衡"。一个 group 中的多个 consumer 可以交错的消费一个 topic 的所有 partitions,简而言之,保证此 topic 的所有 partitions 都能被此 group 所消费,且消费时为了性能考虑,让 partition 相对均衡的分散到每个 consumer 上。

4) Consumer id Registry: 每个consumer都有一个唯一的ID(host:uuid,可以通过配置文件指定,也可以由系统生成),此id用来标记消费者信息。

4) Consumer offset Tracking:用来跟踪每个 consumer 目前所消费的 partition 中最大的offset。此 znode 为持久节点,可以看出 offset 跟 group_id 有关,以表明当 group 中一个消费者失效,其他 consumer 可以继续消费。

5) Partition Owner registry:用来标记 partition 正在被哪个 consumer 消费。临时 znode,此节点表达了"一个partition"只能被 group 下一个 consumer 消费,同时当group下某个consumer失效,那么将会触发负载均衡(即:让partitions在多个consumer间均衡消费,接管那些"游离"的partitions)

当 consumer 启动时,所触发的操作:

A) 首先进行"Consumer id Registry";

B) 然后在"Consumer id Registry"节点下注册一个watch用来监听当前group中其他consumer的"leave"和"join";只要此 znode path 下节点列表变更,都会触发此group下consumer的负载均衡(比如一个consumer失效,那么其他consumer接管partitions)。

C) 在"Broker id registry"节点下,注册一个watch用来监听broker的存活情况;如果broker列表变更,将会触发所有的groups下的consumer重新balance。

总结:

(1)Producer 端使用 zookeeper 用来"发现" broker 列表,以及和 Topic 下每个 partition leader建立socket连接并发送消息.

(2)Broker 端使用 zookeeper 用来注册 broker 信息,已经监测partition leader存活性。

(3) Consumer 端使用 zookeeper 用来注册 consumer 信息,其中包括 consumer 消费的 partition 列表等,同时也用来发现 broker 列表,并和 partition leader 建立 socket 连接,并获取消息。

二、Kafka 架构

在这里插入图片描述
上图中一个 topic 配置了 3 个 partition。Partition1 有两个 offset:0和1。Partition2 有 4 个offset。Partition3 有 1 个 offset。副本的 id 和副本所在的机器的 id 恰好相同。
如果一个 topic 的副本数为 3,那么Kafka将在集群中为每个 partition 创建 3 个相同的副本。集群中的每个 broker 存储一个或多个 partition。多个 producer 和 consumer 可同时生产和消费数据。

Kafka 拓扑结构如下:
在这里插入图片描述

分区原因:

(1)方便在集群中扩展。每个分区可以通过调整以适应它所在的机器,而一个topic又可以由多个分区组成,因而整个集群就可以适应任意大小的数据了;
(2)可以提高并发。因为可以以分区为单位进行读写了。

1、主题 Topic

一个 Topic 可以认为是一类消息,每个 topic 将被分成多个 partition(区),每个 partition 在存储层面是 append log 文件。任何发布到此 partition 的消息都会被直接追加到 log 文件的尾部,每条消息在文件中的位置称为 offset(偏移量),offset 为一个 long 型数字,它是唯一标记一条消息。kafka 并没有提供其他额外的索引机制来存储 offset,因为在 kafka 中几乎不允许对消息进行“随机读写”。

一个主题就是消息的类别或名称。对每个主题,Kafka 集群都管理着一个被分区的日志,如下:
在这里插入图片描述
每个分区就是一个提交日志:每个分区上保存着不断被追加的消息,这些消息是有序的且顺序不可改变;分区上的每个消息都被分配了一个序列号 offset,offset 唯一标识了分区上的消息

在 kafka 中,即使消息被消费,消息仍然不会被立即删除日志文件将会根据 broker 中的配置要求,保留一定的时间之后删除。比如 log 文件保留2 天,那么两天后,文件会被清除,无论其中的消息是否被消费。kafka 通过这种简单的手段,来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘 IO 开支。

对于 consumer 而言,它需要保存消费消息的 offset,对于 offset 的保存和使用, 由consumer 来控制;当 consumer 正常消费消息时,offset 将会"线性"的向前驱动,即消息将依次顺序被消费。事实上consumer 可以使用任意顺序消费消息,它只需要将 offset 重置为任意值。(offset 将会保存在zookeeper 中)

kafka 集群几乎不需要维护任何 consumer 和 producer 状态信息,这些信息由 zookeeper 保存;因此 producer 和 consumer 的客户端实现非常轻量级,它们可以随意离开,而不会对集群造成额外的影响。partitions的设计目的有多个,最根本原因是 kafka 基于文件存储。通过分区,可以将日志内容分散到多个 server 上,来避免文件尺寸达到单机磁盘的上限,每个 partiton 都会被当前 server(kafka实例)保存;可以将一个topic 切分多任意多个 partitions 来保存消息。此外越多的 partitions 意味着可以容纳更多的 consumer,有效提升并发消费的能力。(具体原理参见下文)。这些特性表明,Kafka 的消费者是非常廉价的,一个消费者的创建、销毁不会对集群或其他消费者产生多大的影响。

对日志进行分区有几个目的:

1)扩容,一个主题可以有多个分区,这使得可以保存比一个机器保存的多的多的数据。

2)并行。

2、Partition

topic 中的数据分割为一个或多个 partition。每个 topic 至少有一个 partition。每个 partition 中的数据使用多个 segment 文件存储。partition 中的数据是有序的,不同 partition 间的数据丢失了数据的顺序。如果 topic 有多个 partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将 partition 数目设为1

3、broker

Kafka 集群包含一个或多个服务器,服务器节点称为 broker。
broker 存储 topic 的数据。如果某 topic 有 N 个 partition,集群有 N 个 broker,那么每个 broker 存储该 topic 的一个 partition
如果某 topic 有 N 个 partition,集群有 (N+M) 个 broker,那么其中有 N 个 broker 存储该 topic 的一个 partition,剩下的 M 个 broker 不存储该 topic 的 partition 数据。
如果某 topic 有 N 个 partition,集群中 broker 数目少于 N 个,那么一个 broker 存储该 topic 的一个或多个 partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致 Kafka 集群数据不均衡。

4、Leader 与 Follower

一个 Topic 的多个 partitions,被分布在 kafka 集群中的多个 server 上,每个 server(kafka 实例)负责 partitions 中消息的读写操作。此外 kafka 还可以配置 partitions 需要备份的个数(replicas),每个 partition 将会被备份到多台机器上,以提高可用性

基于 replicated(冗余) 方案,那么就意味着需要对多个备份进行调度。每个 partition 都有一个机器为"leader",零个或多个机器作为 follower。如果 leader 失效,那么将会有其他 follower 来接管(成为新的leader)。leader 负责所有的读写操作,follower 执行 leader 的指令作为 leader 的 server 承载了全部的请求压力follower 只是单调的和 leader 跟进,同步消息即可。Follower 跟随 Leader,所有写请求都通过 Leader 路由,数据变更会广播给所有 Follower,Follower 与 Leader 保持数据同步。如果 Leader 失效,则从 Follower 中选举出一个新的 Leader。当 Follower 与 Leader 挂掉、卡住或者同步太慢,leader 会把这个 follower 从“in sync replicas”(ISR)列表中删除,重新创建一个 Follower。 从集群的整体考虑,有多少个 partitions 就意味着有多少个"leader",kafka 会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定。

Kakfa Broker Leader的选举:Kakfa Broker集群受Zookeeper管理。所有的Kafka Broker节点一起去Zookeeper上注册一个临时节点,因为只有一个Kafka Broker会注册成功,其他的都会失败,所以这个成功在Zookeeper上注册临时节点的这个Kafka Broker会成为Kafka Broker Controller,其他的Kafka broker叫Kafka Broker follower。(这个过程叫Controller在ZooKeeper注册Watch)。这个Controller会监听其他的Kafka Broker的所有信息,如果这个kafka broker controller宕机了,在zookeeper上面的那个临时节点就会消失,此时所有的kafka broker又会一起去Zookeeper上注册一个临时节点,因为只有一个Kafka Broker会注册成功,其他的都会失败,所以这个成功在Zookeeper上注册临时节点的这个Kafka Broker会成为Kafka Broker Controller,其他的Kafka broker叫Kafka Broker follower。例如:一旦有一个broker宕机了,这个kafka broker controller会读取该宕机broker上所有的partition在zookeeper上的状态,并选取ISR列表中的一个replica作为partition leader(如果ISR列表中的replica全挂,选一个幸存的replica作为leader; 如果该partition的所有的replica都宕机了,则将新的leader设置为-1,等待恢复,等待ISR中的任一个Replica“活”过来,并且选它作为Leader;或选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader),这个broker宕机的事情,kafka controller也会通知zookeeper,zookeeper就会通知其他的kafka broker。

5、Producer

生产者即数据的发布者,该角色将消息发布到 Kafka 的 topic 中。broker 接收到生产者发送的消息后,broker 将该消息追加到当前用于追加数据的 segment 文件中。生产者发送的消息,存储到一个 partition 中,生产者也可以指定数据存储的 partition,这可以通过简单的循环的方式来实现,或者使用一些分区方法(比如根据消息的key来分区)。

6、Consumer 与 Consumer group

在这里插入图片描述
多个consumer(consumer 线程)可以组成一个组(Consumer group ),partition 中的每个 message 只能被组(Consumer group )中的一个consumer(consumer 线程)消费,如果一个message可以被多个consumer(consumer 线程)消费的话,那么这些consumer必须在不同的组。Kafka 不支持一个 partition 中的 message 由两个或两个以上的同一个 consumer group 下的consumer thread来处理,除非再启动一个新的consumer group。所以如果想同时对一个 topic 做消费的话,启动多个consumer group就可以了,但是要注意的是,这里的多个 consumer 的消费都必须是顺序读取 partition 里面的 message,新启动的 consumer 默认从 partition 队列最头端最新的地方开始阻塞的读 message。它不能像 AMQ 那样可以多个 BET 作为 consumer 去互斥的(for update悲观锁)并发处理 message,这是因为多个 BET 去消费一个 Queue 中的数据的时候,由于要保证不能多个线程拿同一条 message,所以就需要行级别悲观锁(for update),这就导致了consume 的性能下降,吞吐量不够。而 kafka 为了保证吞吐量,只允许同一个 consumer group 下的一个 consumer 线程去访问一个 partition如果觉得效率不高的时候,可以加 partition 的数量来横向扩展,那么再加新的 consumer thread 去消费如果想多个不同的业务都需要这个 topic 的数据,起多个 consumer group 就好了,大家都是顺序的读取 message,offsite 的值互不影响。这样没有锁竞争,充分发挥了横向的扩展性,吞吐量极高。这也就形成了分布式消费的概念。

当启动一个 consumer group 去消费一个 topic 的时候,无论 topic 里面有多少个 partition,无论我们 consumer group 里面配置了多少个 consumer thread,这个 consumer group 下面的所有 consumer thread 一定会消费全部的 partition;即便这个 consumer group 下只有一个 consumer thread,那么这个 consumer thread 也会去消费所有的 partition。因此,最优的设计就是,consumer group 下的 consumer thread 的数量等于 partition 数量,这样效率是最高的
一个 consumer group 下,无论有多少个 consumer,这个 consumer group 一定会把这个 topic 下所有的 partition 都消费了。当 consumer group 里面的 consumer 数量小于这个 topic 下的 partition 数量的时候,就会出现一个 conusmer thread 消费多个 partition 的情况,总之是这个 topic 下的 partition 都会被消费。如果 consumer group 里面的 consumer 数量等于这个 topic 下的 partition 数量的时候,此时效率是最高的,每个 partition 都有一个 consumer thread 去消费。当consumer group里面的consumer数量大于这个topic下的partition数量的时候,就会有一个consumer thread空闲。因此,我们在设定 consumer group 的时候,只需要指明里面有几个 consumer 数量即可,无需指定对应的消费 partition 序号,consumer 会自动进行 rebalance

Consumer Rebalance的触发条件:(1)Consumer增加或删除会触发 Consumer Group的Rebalance(2)Broker的增加或者减少都会触发 Consumer Rebalance

多个 Consumer Group 下的 consumer 可以消费同一条 message,所以一定会重复消费这批 message 的

如果 producer 的流量增大,为了保证 topic 的 parition 数量 = consumer 数量,这时候的应对方式就是扩展:增加 topic 下的 partition,同时增加这个 consumer group 下的consumer。

三、Kafka 文件存储

1、基本概念

(1)Broker:消息中间件处理结点,一个Kafka节点就是一个 broker,多个 broker 能够组成一个Kafka集群。

(2)Topic:一类消息,比如 page view 日志、click日志等都能够以topic的形式存在。Kafka集群能够同一时候负责多个topic的分发。

(3)Partition:topic物理上的分组。一个topic能够分为多个partition,每一个partition是一个有序的队列。

(4)Segment:partition物理上由多个segment组成。以下有具体说明。

(5)offset:每一个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每一个消息都有一个连续的序列号叫做offset,用于partition中唯一标识的这条消息。

2、topic 中 partition 存储分布

如果实验环境中 Kafka 集群仅仅有一个 broker。xxx/message-folder 为数据文件存储根文件夹。
在 Kafka broker 中的 server.properties 进行文件配置(參数 log.dirs=xxx/message-folder)。比如创建 2 个 topic 名称分别为 report_push、launch_info,partitions 数量都为 partitions=4。

存储路径和文件夹规则为:xxx/message-folder

partiton 中文件存储方式:
在这里插入图片描述
在 Kafka 文件存储中,同一个 topic 下有多个不同 partition,每一个 partition 为一个文件夹,partiton 命名规则为 topic 名称+有序序号,第一个 partiton 序号从0開始,序号最大值为 partitions 数量减1

如果是多broker分布情况,请參考kafka集群partition分布原理分析

3、partiton中文件存储方式

partition中文件存储方式:
在这里插入图片描述
每一个 partion(文件夹)相当于一个巨型文件被平均分配到多个大小相等 segment (段)数据文件里
但每一个段 segment file 消息数量不一定相等,这样的特性方便 old segment file 高速被删除(默认情况下每一个文件大小为1G)。
每一个 partiton 仅仅须要支持顺序读写即可了。
segment 文件生命周期由服务端配置參数决定。比如它在默认情况下,每满500兆就会创建新的 segment 段(segment file),每满7天就会清理之前的数据。
这样做的优点就是能高速删除无用文件,有效提高磁盘利用率

为防止 log 文件过大导致数据定位效率低下,kafka 采取了分片和索引机制,将每个分区分为多个片段(segment),每个 segment 对应两个文件 —— .index 和 .log 文件。

4、partiton 中 segment 文件存储结构

segment file 组成:由2大部分组成,分别为 index file 和 data file,此2个文件一一相应,成对出现,后缀 ”.index” 和 “.log” 分别表示为 segment 索引文件、数据文件。

segment 文件命名规则:partion 全局的第一个 segment 从0开始,每一个 segment 文件名称为上一个 segment 文件最后一条消息的 offset 值。

索引文件(index文件)中存储大量的元数据,而数据文件(log文件)中存储大量的消息。索引文件(index文件)中的元数据指向对应的数据文件(log文件)中消息的物理偏移地址。
在这里插入图片描述

上图的左半部分是索引文件,里面存储的是一对一对的 key-value,其中 key 是消息在数据文件(对应的log文件)中的编号,比如“1,3,6,8……”,分别表示在log文件中的第1条消息、第3条消息、第6条消息、第8条消息……,那么为什么在 index 文件中这些编号不是连续的呢?这是因为 index 文件中并没有为数据文件中的每条消息都建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引,这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。
其中以索引文件中元数据3,497为例,其中3代表在右边log数据文件中从上到下第3个消息(在全局partiton表示第368772个消息),其中497表示该消息的物理偏移地址(位置)为497。

5、在 partition 中通过 offset 查找 message

比如读取 offset=368776的message,须要通过以下 2 个步骤查找。

(1)第一步查找 segment file
在这里插入图片描述

以上图为例,当中 00000000000000000000.index 表示最还开始的文件,起始偏移量(offset)为0,第二个文件 00000000000000368769.index 的消息量起始偏移量为 368770 = 368769 + 1. 相同,第三个文件 00000000000000737337.index 的起始偏移量为737338=737337 + 1,其它文件依次类推。以起始偏移量命名并排序这些文件,仅仅要依据 offset 二分查找文件列表,就能够高速定位到具体文件。 当 offset=368776 时定位到 00000000000000368769.index|log。

(2)第二步通过 segment file 查找 message

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

segment index file 采取稀疏索引存储方式,它降低索引文件大小。通过 mmap 能够直接内存操作,稀疏索引为数据文件的每一个相应 message 设置一个元数据指针,它比稠密索引节省了很多其它的存储空间,但查找起来须要消耗很多其它的时间

总结

Kafka高效文件存储设计特点:

(1) Kafka把topic中一个parition大文件分成多个小文件段。通过多个小文件段,就easy定期清除或删除已经消费完文件,降低磁盘占用。

(2)通过索引信息能够高速定位message和确定response的最大大小。

(3)通过index元数据所有映射到memory,能够避免segment file的IO磁盘操作。

(4)通过索引文件稀疏存储,能够大幅降低index文件元数据占用空间大小。

四、Kafka 的可靠性、一致性与幂等性

1、kafka 数据可靠性保证(不丢数据)

为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个分区收到 producer 发送的数据之后,都需要向 producer 发送acks(acknowledgement 确认收到),如果 producer 没有收到acks,将会重新发送数据,否者进行下一轮发送。

kafka 采用所有副本同步完成之后,才发送acks(这样做延迟高,但是重新选举 leader 时,容忍 n 台节点的故障,只需要 n+1 个副本就可以了)。但是可能出现 leader 收到数据,所有 follower 都开始同步数据,但有一个 follower 因为某种故障,迟迟不能与 leader 通信, leader 将会一直等下去,为了解决这个问题,leader 维护了一个动态的 ISR,表示和 leader保持同步的 follower 集合。当 ISR 中的 follower 完成数据同步之后,leader 就会给 producer 发送 acks,当 ISR 中的 follower 超过 replica.lag.time.max.ms 设置的时间,为没有向 leader 同步数据,则将该 follower 剔除。leader 发生故障后,就会从 ISR 中重新选举leader

对于某些不太重要的参数,可以容忍数据的少量丢失,所以没必要等到 ISR 中的所有 follower 同步成功。

ack 参数配置:

1) 0:producer 不等待 acks,延迟低,当 broker 出现故障可能丢失数据;

2) 1:producer 等待acks,分区的 leader 写入磁盘成功后返回 acks(只等 leader 写完,不管 follower),若在 follower 同步成功之前 leader 故障,将会丢数据;

3) -1:producer 等待 acks,分区的 leader 和 ISR 中的 follower 全部写入成功才返回 acks,若 follower 同步完成后,broker 发送 acks 之前,leader 故障,将会造成数据重复。

2、kafka 数据一致性(消费者消费和 kafka 存储)

(LEO:指的是每个副本最大的 offset(偏移量),HW:指的是 ISR 队列中最小的 offset,也就是消费者能见到的最大的 offset)

(1)follower 故障:follower 发生故障后会被踢出 ISR 队列,等 follower 恢复后,follower 会读取本地磁盘记录的挂掉之前的 HW,并将 log 文件高于 HW 的截掉,然后从 HW 的位置开始向 leader 同步,等该 follower 的 LED 大于等于该分区的 HW,即 follower 追上 leader 之后,就可以重新加入 ISR 队列了。

(2)leader 故障:leader 发生故障之后,会从 ISR 中选出新的 leader,然后其余的 follower 会先将各自 log 文件高于 HW 的部分截掉,接着从新的 leader 同步数据。

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

3、kafka 的幂等性

kafka 的幂等性指的是无论 producer 发送多少次,broker 只会存储一次
kafka 中设置 enable.idempotence 设置为 true 时,即可保证幂等性,此时 acks 默认是 -1 。开启幂等性之后,producer 会在初始化的时候分配一个 PID,发往同一个分区的信息会携带一个 seqNumber,而 broker 端会对 <PID, Partiton, SeqNumber> 做缓存,当具有相同的 SeqNumber 消息时,broker 之后保存一条。但是 PID 每次重启都会变化,不同的分区也具有 不同的主键,所有幂等性无法保证跨分区会话的幂等性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值