常规篇
0、kafka 是什么?有什么作用?
- Kafka 是一个分布式的流式处理平台,它以高吞吐、可持久化、可水平扩展、支持流数据处理等多种特性而被广泛使用。主要功能体现于三点:
- 消息系统:kafka与传统的消息中间件都具备系统解耦、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等功能。与此同时,kafka还提供了大多数消息系统难以实现的消息顺序性保障及回溯性消费的功能。
- 存储系统:kafka把消息持久化到磁盘,相比于其他基于内存存储的系统而言,有效的降低了消息丢失的风险。这得益于其消息持久化和多副本机制。也可以将kafka作为长期的存储系统来使用,只需要把对应的数据保留策略设置为“永久”或启用主题日志压缩功能。
- 流式处理平台:kafka为流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理框架,比如窗口、连接、变换和聚合等各类操作。
1、kafka的核心组件有哪些,分别有什么作用?
- Producer:即消息生产者,向 Kafka Broker 发消息的客户端。
- Consumer:即消息消费者,从 Kafka Broker 读消息的客户端。
- Consumer Group:即消费者组,由多个 Consumer 组成。消费者组内每个消费者负责消费不同分区的数据,以提高消费能力。一个分区只能由组内一个消费者消费,不同消费者组之间互不影响。
- Broker:一台 Kafka 服务节点就是一个 Broker。一个集群是由1个或者多个 Broker 组成的,且一个 Broker 可以容纳多个 Topic。
- Topic:一个逻辑上的概念,Topic 将消息分类,生产者和消费者面向的都是同一个 Topic, 同一个 Topic 下的 Partition 的消息内容是不相同的。
- Partition:为了实现 Topic 扩展性,提高并发能力,一个非常大的 Topic 可以分布到多个 Broker 上,一个 Topic 可以分为多个 Partition 进行存储,且每个 Partition 是消息内容是有序的。
- Replica:即副本,为实现数据备份的功能,保证集群中的某个节点发生故障时,该节点上的 Partition 数据不丢失,且 Kafka 仍然能够继续工作,为此 Kafka 提供了副本机制,一个 Topic 的每个 Partition 都有若干个副本,一个 Leader 副本和若干个 Follower 副本。
- Leader:即每个分区多个副本的主副本,生产者发送数据的对象,以及消费者消费数据的对象,都是 Leader。
- Follower:即每个分区多个副本的从副本,会实时从 Leader 副本中同步数据,并保持和 Leader 数据的同步。Leader 发生故障时,某个 Follower 还会被选举并成为新的 Leader , 且不能跟 Leader 在同一个 Broker 上, 防止崩溃数据可恢复。
- Offset:消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从消费位置继续消费。
2、如何确定当前能读到哪一条消息?
- 分区相当于一个日志文件,我们先简单介绍几个概念,如下图是一个分区日志文件。
- 标识共有7条消息,offset (消息偏移量)分别是0~6
- 0 代表这个日志文件的开始
- HW(High Watermark) 为4,0~3 代表这个日志文件可以消费的区间,消费者只能消费到这四条消息
- LEO 代表即将要写入消息的偏移量 offset
- 分区 ISR 集合中的每个副本都会维护自己的 LEO,而 ISR 集合中最小的LEO 即为分区的 HW。
- 如上图: 三个分区副本都是 ISR集合当中的,最小的 LEO 为 3,就代表分区的 HW 为3,所以当前分区只能消费到 0~2 之间的三条数据,如下图
3、生产者发送消息有哪些模式?
- 发后即忘(fire-and-forget): 它只管往 Kafka 里面发送消息,但是不关心消息是否正确到达,这种方式的效率最高,但是可靠性也最差,比如当发生某些不可充实异常的时候会造成消息的丢失
- 同步(sync): producer.send()返回一个Future对象,调用get()方法变回进行同步等待,就知道消息是否发送成功,发送一条消息需要等上个消息发送成功后才可以继续发送
- 异步(async): Kafka支持 producer.send() 传入一个回调函数,消息不管成功或者失败都会调用这个回调函数,这样就算是异步发送,我们也知道消息的发送情况,然后再回调函数中选择记录日志还是重试都取决于调用方
4、发送消息的分区策略有哪些?
- 轮询:依次将消息发送该topic下的所有分区,如果在创建消息的时候 key 为 null,Kafka 默认采用这种策略。
- key 指定分区:在创建消息是 key 不为空,并且使用默认分区器,Kafka 会将 key 进行 hash,然后根据hash值映射到指定的分区上。这样的好处是 key 相同的消息会在一个分区下,Kafka 并不能保证全局有序,但是在每个分区下的消息是有序的,按照顺序存储,按照顺序消费。在保证同一个 key 的消息是有序的,这样基本能满足消息的顺序性的需求。但是如果 partation 数量发生变化,那就很难保证 key 与分区之间的映射关系了。
- 自定义策略:实现 Partitioner 接口就能自定义分区策略。
- 指定 Partiton 发送
5、Kafka 支持读写分离吗?为什么?
- Kafka 是不支持读写分离的,那么读写分离的好处是什么?主要就是让一个节点去承担另一个节点的负载压力,也就是能做到一定程度的负载均衡,而且 Kafka 不通过读写分离也可以一定程度上去实现负载均衡。
- 但是对于 Kafka 的架构来说,读写分离有两个很大的缺点:
- 数据不一致的问题:读写分离必然涉及到数据的同步,只要是不同节点之间的数据同步,必然会有数据不一致的问题存在。
- 延时问题:由于 Kafka 独特的数据处理方式,导致如果将数据从一个节点同步到另一个节点必然会经过主节点磁盘和从节点磁盘,对一些延时性要求较高的应用来说,并不太适用。
6、Kafka 的消息消费方式有哪些?
- 点对点: 如果所有消费者属于同一个消费组,那么所有的消息都会被均匀的投递给每一个消费者,每条消息只会被其中一个消费者消费。
- 发布订阅: 如果所有消费者属于不同的消费组,那么所有的消息都会被投递给每一个消费者,每个消费者都会收到该消息。
7、如何增强消费者的消费能力?
- 可以考虑增加 topic 的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。
- 如果是消费者消费不及时,可以采用多线程的方式进行消费,并且优化业务方法流程。
8、消费者与 topic 的分区分配策略有哪些?
- RangeAssignor 分配策略:该分配策略是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后分区按照跨度来进行平均分配,尽可能保证分区均匀的分配给所有的消费者。
- RoundRobinAssignor 分配策略:该分配策略是按将消费者组内所有消费者及消费者订阅的所有主题的分区按照字典排序,然后通过轮询的方式分配给每个消费者。
- StickyAssignor分配策略:这种分配策略有两个目的:分区的分配要尽可能的均匀;分区的分配尽可能的与上次分配的保持相同。当两者发生冲突时,第一个目标优先于第二个目标。
- 自定义分区分配策略:可以通过实现 org.apache.kafka.clients.consumer.internals.PartitionAssignor 接口来实现
注:详细内容请点击这里。
9、kafka 控制器是什么?有什么作用?
- 在 Kafka 集群中会有一个或多个 broker,其中有一个 broker 会被选举为控制器,它负责管理整个集群中所有分区和副本的状态,kafka 集群中只能有一个控制器。
- 当某个分区的 leader 副本出现故障时,由控制器负责为该分区选举新的 leader 副本。
- 当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有 broker 更新其元数据信息。
- 当为某个 topic 增加分区数量时,由控制器负责分区的重新分配。
10、什么是消费者组,有什么作用?
- 定义: 就是由多个 Consumer 组成,且拥有一个公共且唯一的 Group ID。组内每个消费者负责消费不同分区的数据,但一个分区只能由一个组内消费者消费,消费者组之间互不影响。
- 作用:
- 每个消费者组之间可以消费相同的分区数据,这样可以实现发布订阅机制。
- 每个消费者组内部的消费者只能消费不同的分区数据,一来可以提高消费吞吐量,二来可以保证每个消费者消费数据互不影响。
11、Kafka 中 Offset的作用是什么,如何维护?
- 在 Kafka 中每个 Topic 分区下面的每条消息都被赋予了一个唯一的ID值,用来标识它在分区中的位置。这个ID值就被称为位移Offset或者叫偏移量,一旦消息被写入到日志分区中,它的位移值将不能被修改。
原理篇
1、那 Kafka 是怎么去实现负载均衡的?
- Kafka 的负责均衡主要是通过分区来实现的,我们知道 Kafka 是主写主读的架构,如下图:
- 共三个 broker ,里面各有三个副本,总共有三个 partation, 深色的是 leader,浅色的是 follower,上下灰色分别代表生产者和消费者,虚线代表 follower 从 leader 拉取消息。
- 我们从这张图就可以很明显的看出来,每个 broker 都有消费者拉取消息,每个 broker 也都有生产者发送消息,每个 broker 上的读写负载都是一样的,这也说明了 kafka 独特的架构方式可以通过主写主读来实现负载均衡。
2、Kafka 的负责均衡会有什么问题呢?
- broker 端分配不均:当创建 topic 的时候可能会出现某些 broker 分配到的分区数多,而有些 broker 分配的分区少,这就导致了 leader 多副本不均。
- 生产者写入消息不均:生产者可能只对某些 broker 中的 leader 副本进行大量的写入操作,而对其他的 leader 副本不闻不问。
- 消费者消费不均:消费者可能只对某些 broker 中的 leader 副本进行大量的拉取操作,而对其他的 leader 副本不闻不问。
- leader 副本切换不均:当主从副本切换或者分区副本进行了重分配后,可能会导致各个 broker 中的 leader 副本分配不均匀。
3、kafka 控制器是怎么进行选举的?
- kafka(2.8版本之前)中的控制器选举工作依赖于 Zookeeper,成功竞选成为控制器的 broker 会在Zookeeper中创建/controller临时节点。
- 每个 broker 启动的时候会去尝试读取/controller 节点的 brokerid的值
- 如果读取到的 brokerid 的值不为-1,表示已经有其他broker 节点成功竞选为控制器,所以当前 broker 就会放弃竞选;
- 如果Zookeeper中不存在/controller 节点,或者这个节点的数据异常,那么就会尝试去创建/controller 节点,创建成功的那个 broker 就会成为控制器。
4、kafka 为什么这么快?
- 顺序读写: 磁盘分为顺序读写与随机读写,基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,kafka 这里采用的就是顺序读写。
- Page Cache: 为了优化读写性能,Kafka 利用了操作系统本身的 Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。
- 零拷贝: Kafka使用了零拷贝技术,也就是直接将数据从内核空间的读缓冲区直接拷贝到内核空间的 socket 缓冲区,然后再写入到 NIC 缓冲区,避免了在内核空间和用户空间之间穿梭。
- 分区分段+索引:
- Kafka 的 message 是按 topic分 类存储的,topic 中的数据又是按照一个一个的 partition 即分区存储到不同 broker 节点。每个 partition 对应了操作系统上的一个文件夹,partition 实际上又是按照segment分段存储的。
- 通过这种分区分段的设计,Kafka 的 message 消息实际上是分布式存储在一个一个小的 segment 中的,每次文件操作也是直接操作的 segment。为了进一步的查询优化,Kafka 又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。
- 批量读写: Kafka 数据读写也是批量的而不是单条的,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。
- 批量压缩: Kafka 把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络 IO 损耗,通过 mmap 提高 I/O 速度,写入数据的时候由于单个Partion是末尾添加所以速度最优;读取数据的时候配合 sendfile 进行直接读取。
5、Kafka分区Leader选举机制是如何进行的?
- 创建分区时指定的优先leader。如果不指定,则为分区的第一个副本。
- Kafka会在Zookeeper上针对每个Topic维护一个称为ISR(in-sync replica,已同步的副本)的集合,该集合中是一些分区的副本。只有当这些副本都跟Leader中的副本同步了之后,kafka才会认为消息已提交,并反馈给消息的生产者。如果这个集合有增减,kafka会更新zookeeper上的记录。如果某个分区的Leader不可用,Kafka就会从ISR集合中选择一个副本作为新的Leader。
架构篇
1、kafka 的架构是怎么样的?
- 一个典型的 kafka 体系架构包括若干 Producer、若干 Consumer、以及一个 Zookeeper 集群(在2.8.0版本中移,除了 Zookeeper,通过 KRaft 进行自己的集群管理)
- Producer 将消息发送到 Broker,Broker 负责将受到的消息存储到磁盘中,而 Consumer 负责从 Broker 订阅并消费消息。
2、在kafka中zookeeper的作用是什么?
- Broker注册: Broker是分布式部署并且相互之间相互独立,但是需要有一个注册系统能够将整个集群中的Broker管理起来,此时就使用到了Zookeeper。在Zookeeper上会有一个专门用来进行Broker服务器列表记录的节点:/brokers/ids。
- Topic注册: 在Kafka中,同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也都是由Zookeeper在维护,由专门的节点来记录,如:/borkers/topics。
- 生产者负载均衡: 由于每个Broker启动时,都会完成Broker注册过程,生产者会通过该节点的变化来动态地感知到Broker服务器列表的变更,这样就可以实现动态的负载均衡机制。
- 消费者负载均衡: 每个消费者分组包含若干消费者,每条消息都只会发送给分组中的一个消费者,不同的消费者分组消费自己特定的Topic下面的消息,互不干扰。
- 维护分区与消费者的关系: 对于每个消费者组,Kafka都会为其分配一个全局唯一的Group ID,Group 内部的所有消费者共享该 ID。订阅的topic下的每个分区只能分配给某个 group 下的一个consumer(当然该分区还可以被分配给其他group)。同时,Kafka为每个消费者分配一个Consumer ID,通常采用"Hostname:UUID"形式表示。
- 维护消息消费进度Offset记录: 在消费者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度Offset记录到Zookeeper上,以便在该消费者进行重启或者其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。
- 消费者注册: 每个消费者服务器启动时,都会到Zookeeper的指定节点下创建一个属于自己的消费者节点,例如/consumers/[group_id]/ids/[consumer_id],完成节点创建后,消费者就会将自己订阅的Topic信息写入该临时节点。
注:详细内容请点击这里。
3、Kafka Replicas是怎么管理的?
- 基本概念:
- AR:分区中的所有 Replica 统称为 AR
- ISR:所有与 Leader 副本保持一定程度同步的Replica(包括 Leader 副本在内)组成 ISR
- OSR:与 Leader 副本同步滞后过多的 Replica 组成了 OSR
- Leader 负责维护和跟踪 ISR 集合中所有 Follower 副本的滞后状态,当 Follower 副本落后过多时,就会将其放入 OSR 集合,当 Follower 副本追上了 Leader 的进度时,就会将其放入 ISR 集合。
- 默认情况下,只有 ISR 中的副本才有资格晋升为 Leader。
4、Kafka 的可靠性是怎么保证的?
- acks: 这个参数用来指定分区中有多少个副本收到这条消息,生产者才认为这条消息是写入成功的,这个参数有三个值:
- acks = 1:默认为1,生产者发送消息,只要 leader 副本成功写入消息,就代表成功。
- acks = 0:生产者发送消息后直接算写入成功,不需要等待响应。
- acks = -1 或 acks = all:生产者发送消息后,需要等待 ISR 中的所有副本都成功写入消息后才能收到服务端的响应。毫无疑问这种方案的可靠性是最高的,但是吞吐性能也会受到影响。
- 消息发送的方式: 生产者发送消息有三种方式,发完即忘,同步和异步。我们可以通过同步或者异步获取响应结果,失败做重试来保证消息的可靠性。
- 手动提交位移: 默认情况下,当消费者消费到消息后,就会自动提交位移。但是如果消费者消费出错,没有进入真正的业务处理,那么就可能会导致这条消息消费失败,从而丢失。我们可以开启手动提交位移,等待业务正常处理完成后,再提交offset。
- 通过副本 LEO 来确定分区 HW: 分区 ISR 集合中的每个副本都会维护自己的 LEO,而 ISR 集合中最小的LEO 即为分区的 HW。
5、分区再分配是做什么的?解决了什么问题?
- 问题1:当集群中的一个节点下线了
- 如果该节点的分区是单副本的,那么分区将会变得不可用
- 如果是多副本的,就会进行 leader 选举,在其他机器上选举出新的 leader
- kafka 并不会将这些失效的分区迁移到其他可用的 broker 上,这样就会影响集群的负载均衡,甚至也会影响服务的可靠性和可用性
- 问题2:当集群新增 broker 时,只有新的主题分区会分配在该 broker 上,而老的主题分区不会分配在该 broker 上,就造成了老节点和新节点之间的负载不均衡。
- 分区再分配: 为了解决上述问题就出现了分区再分配,它可以在集群扩容,broker 失效的场景下进行分区迁移。
- 原理: 分区再分配的原理就是通过控制器给分区新增新的副本,然后通过网络把旧的副本数据复制到新的副本上,在复制完成后,将旧副本清除。 为了不影响集群正常的性能,在此复制期间还会有一些列保证性能的操作,比如复制限流。
6、副本 leader 是怎么选举的?
- 背景: 当分区 leader 节点崩溃时,其中一个 follower 节点会成为新的 leader 节点,这样会导致集群的负载不均衡,从而影响服务的健壮性和稳定性。
- 优先副本: 优先副本就是说在 AR 集合中的第一个副本。比如分区 2 的 AR 为 0,1,2,那么分区 2 的优先副本就为0。理想情况下优先副本就是 leader 副本。优先副本选举就是促使优先副本成为 leader 副本,从而维护集群的负载均衡。
7、分区数越多越好吗?吞吐量就会越高吗?
- 这个不一定的,还是要根据实际情况设置一个合理的分区数,分区数过多的话会有以下几个问题:
- 客户端/服务器端需要使用的内存就越多
- 文件句柄的开销:每个 broker 会为每个日志段文件打开一个 index 文件句柄和一个数据文件句柄。因此,随着 partition 的增多,所需要保持打开状态的文件句柄数也就越多,最终可能超过底层操作系统配置的文件句柄数量限制。
- 越多的分区可能增加端对端的延迟
- 降低高可用性:前面提到了分区再分配,会将数据复制到另一份副本当中,分区数量越多,那么恢复时间也就越长。
8、什么情况下 kafka 会丢失消息?
- 生产者发送数据: acks设置为0或者1时,会有可能导致消息丢失。
- Broker存储数据:kafka 通过 Page Cache 将数据写入磁盘。Page Cache 就是当往磁盘文件写入的时候,系统会先将数据流写入缓存中,但是什么时候将缓存的数据写入文件中是由操作系统自行决定。所以如果此时机器突然挂了,也是会丢失消息的。
- 消费者消费数据:在开启自动提交 offset 时,只要消费者消费到消息,那么就会自动提交偏移量,如果业务还没有来得及处理,那么消息就会丢失。
9、Kafka高吞吐的日志存储架构是如何设计的?
- kafka 中消息是以主题 Topic 为基本单位进行归类的,这里的 Topic 是逻辑上的概念,实际上在磁盘存储是根据分区 Partition 存储的, 即每个 Topic 被分成多个 Partition,分区 Partition 的数量可以在主题 Topic 创建的时候进行指定。
- Partition 分区主要是为了解决 Kafka 存储的水平扩展问题而设计的, 如果一个 Topic 的所有消息都只存储到一个 Kafka Broker上的话, 对于 Kafka 每秒写入几百万消息的高并发系统来说,这个 Broker 肯定会出现瓶颈, 故障时候不好进行恢复,所以 Kafka 将 Topic 的消息划分成多个 Partition, 然后均衡的分布到整个 Kafka Broker 集群中。
- Partition 分区内每条消息都会被分配一个唯一的消息 id,即我们通常所说的 偏移量 Offset, 因此 kafka 只能保证每个分区内部有序性,并不能保证全局有序性。
- 然后每个 Partition 分区又被划分成了多个 LogSegment,这是为了防止 Log 日志过大,Kafka 又引入了日志分段(LogSegment)的概念,将 Log 切分为多个 LogSegement,相当于一个巨型文件被平均分割为一些相对较小的文件,这样也便于消息的查找、维护和清理。这样在做历史数据清理的时候,直接删除旧的 LogSegement 文件就可以了。
- Log 日志在物理上只是以文件夹的形式存储,而每个 LogSegement 对应磁盘上的一个日志文件和两个索引文件,以及可能的其他文件(比如以".snapshot"为后缀的快照索引文件等)
注:详细内容请点击这里。
10、Kafka的线上集群部署方案是如何做的?
- 需求场景分析: 评估每天的请求量,根据二八法则算出峰值QPS,计算每天存储空间,确定副本数。
- 确定物理机数量: 根据峰值QPS来确定,并且适当冗余一些。
- 确定磁盘类型: Kafka 写磁盘是顺序追加写的,所以使用普通机械硬盘就可以了。
- 确定每台服务器硬盘数量: 考虑数据存储量以及服务性能和稳定性,确定硬盘数量。
- 评估内存: Kafka 读写数据的流程主要都是基于os cache,还需要分配一定的内存给 JVM 以保证服务的稳定性。
- 评估CPU: 我们评估需要多少个 CPU Core,主要是看 Kafka 进程里会有多少个线程,线程主要是依托多核CPU来执行的,如果线程特别多,但是 CPU核很少,就会导致CPU负载很高,会导致整体工作线程执行的效率不高,性能也不会好。所以我们要保证CPU Core的充足, 来保障系统的稳定性和性能最优。
- 评估网卡: 一般情况下,网卡带宽是不会达到上限的, 千兆网卡基本可以满足, 万兆网卡更好,实际要根据峰值QPS和数据量来估算。
- 集群部署规划: 根据之前的分析来确定服务器数量,然后来构建 Kafka 集群,集群依赖 ZooKeeper,所以在部署 Kafka 之前,需要部署好 ZooKeeper 集群。这里我将 Kafka 和 ZooKeeper 部署在了一起,Kafka 集群节点操作系统仍然采用 Centos 7.7 版本。
注:详细内容请点击这里。
方案篇
1、如何保证kafka的消息是有序的?
- Kafka 并不能保证全局有序,但是在每个分区下的消息是有序的,按照顺序存储,按照顺序消费。在保证同一个 key 的消息是有序的,这样基本能满足消息的顺序性的需求。
2、Kafka上有大量消息积压,如何处理?
- 实时/消费任务挂掉导致的消费积压的解决方法
- 任务重新启动后直接消费最新的消息,对于"滞后"的历史数据采用离线程序进行"补漏"。
- 如下面图所示。创建新的topic并配置更多数量的分区,将积压消息的topic消费者逻辑改为直接把消息打入新的topic,将消费逻辑写在新的topic的消费者中。
- Kafka分区数设置的不合理或消费者"消费能力"不足的优化
- Kafka分区数是Kafka并行度调优的最小单元,如果Kafka分区数设置的太少,会影响Kafka Consumer消费的吞吐量。
- 如果数据量很大,Kafka消费能力不足,则可以考虑增加Topic的Partition的个数,同时提升消费者组的消费者数量。
- Kafka消息key设置的优化
- 使用Kafka Producer消息时,可以为消息指定key,但是要求key要均匀,否则会出现Kafka分区间数据不均衡。
- 所以根据业务,合理修改Producer处的key设置规则,解决数据倾斜问题。
注:详细内容请点击这里。