kafka的topic面试题

2024-05-07 08:19:59 浏览数 (1)

1. topic

1.1. 简单描述一下Kafka
  • Kafka是一个高吞吐、易扩展的分布式发布-订阅消息系统,它能够将消息持久化到磁盘,用于批量的消费。Kafka中有以下几个概念:
    • Topic:特指Kafka处理的消息源(feeds of messages)的不同分类。
    • Partition:Topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。
    • Broker:Kafa集群中包含一台或多台服务器,这种服务器被称为broker。
    • Producer:生产者,向Kafka的一个topic发布消息。
    • Consumers:消费者,从kafka的某个topic读取消息。
1.2. 对于发布-订阅消息系统有很多选择,是什么促使 Apache Kafka 是一个很好的选择呢?
  • 多个生产者:无论 kafka 多个生产者的客户端正在使用很多 topic 还是同一个 topic ,Kafka 都能够无缝处理好这些生产者。这使得 kafka 成为一个从多个前端系统聚合数据, 然后提供一致的数据格式的理想系统. 例如, 一个通过多个微服务向用户提供内容的站点, 可以为统计 page view 而只设立一个 topic, 所有的服务将 page view 以统一的格式写入这个 topic. 消费程序能够以统一的数据格式来接收 page view 数据, 而不需要去协调多个生产者流.
  • 多个消费者:除了多个生产者之外,kafka 也被设计为多个消费者去读取任意的单个消息流而不相互影响;而其他的很多消息队列系统,一旦一个消息被一个客户端消费, 那么这个消息就不能被其他客户端消费,这是 kafka 与其他队列不同的地方;同时多个 kafka 消费者也可以选择作为一个组的一部分,来分担一个消息流,确 保这整个组,这个消息只被消费一次.
1.3. topic的分区数可不可以增加?如果可以怎么增加?如果不可以,那又是为什么?
  • 可以增加,使用 kafka-topics 脚本,结合 --alter 参数来增加某个主题的分区数,命令如下:
代码语言:javascript复制
bin/kafka-topics.sh --bootstrap-server broker_host:port --alter --topic <topic_name> --partitions <新分区数>
1.4. topic的分区数可不可以减少?如果可以怎么减少?如果不可以,那又是为什么?
  • 不支持,因为删除的分区中的消息不好处理。如果直接存储到现有分区的尾部,消息的时间戳就不会递增,如此对于 Spark、Flink 这类需要消息时间戳(事件时间)的组件将会受到影响;如果分散插入现有的分区,那么在消息量很大的时候,内部的数据复制会占用很大的资源,而且在复制期间,此主题的可用性又如何得到保障?与此同时,顺序性问题、事务性问题,以及分区和副本的状态机切换问题都是不得不面对的。
1.5. 创建 topic 时如何选择合适的分区数?
  • 多个消费者中有人空闲了,那么分区数量应该大于等于一个消费者群组下的消费者的数量。
  • 想充分发挥多个broker的性能,那么分区数量应该大于等于broker的数量

2. kafka

2.1. Kafka相对传统技术有什么优势?
  • 快速:单一的Kafka代理可以处理成千上万的客户端,每秒处理数兆字节的读写操作。
  • 持久:消息是持久性的,并在集群中进行复制,以防止数据丢失。
  • 设计:它提供了容错保证和持久性
2.2. Kafka生产者客户端中使用了几个线程来处理?分别是什么?
  • 整个生产者客户端由两个线程协调运行,这两个线程分别为主线程和 Sender 线程(发送线程)。在主线程中由 KafkaProducer 创建消息,然后通过可能的拦截器、序列化器和分区器的作用之后缓存到消息累加器(RecordAccumulator,也称为消息收集器)中。Sender 线程负责从 RecordAccumulator 中获取消息并将其发送到 Kafka 中。
2.3. Kafka生产者客户端的整体结构是什么样子的?

2.4. 为什么kafka不支持主从分离?为什么不像redis和mysql可以支持主从分离呢,是因为什么原因要这么设计呢?
  • 对于那种读操作很多而写操作相对不频繁的负载类型而言,采用读写分离是非常不错的方案——我们可以添加很多follower横向扩展,提升读操作性能。反观Kafka,它的主要场景还是在消息引擎而不是以数据存储的方式对外提供读服务,通常涉及频繁地生产消息和消费消息,这不属于典型的读多写少场景,因此读写分离方案在这个场景下并不太适合。
  • Kafka副本机制使用的是异步消息拉取,因此存在leader和follower之间的不一致性。如果要采用读写分离,必然要处理副本lag引入的一致性问题,比如如何实现read-your-writes、如何保证单调读(monotonic reads)以及处理消息因果顺序颠倒的问题。相反地,如果不采用读写分离,所有客户端读写请求都只在Leader上处理也就没有这些问题了——当然最后全局消息顺序颠倒的问题在Kafka中依然存在,常见的解决办法是使用单分区,其他的方案还有version vector,但是目前Kafka没有提供。
2.5. 如何清除kafka所有的缓存信息
  1. 关闭集群和ZooKeeper
  2. 删除log.dirs配置的目录下的内容
  3. 删除ZooKeeper路径下的内容
  4. 重启ZooKeeper和集群
2.6. kafka特点
  • Kafka具有近乎实时性的消息处理能力,面对海量消息也能够高效地存储消息和查询消息。Kafka将消息保存在磁盘中,在其设计理念中并不惧怕磁盘操作,它以顺序读写的方式访问磁盘,从而避免了随机读写磁盘导致的性能瓶颈。
  • Kafka支持批量读写消息,并且会对消息进行批量压缩,这样既提高了网络的利用率,也提高了压缩效率。
  • Kafka支持消息分区,每个分区中的消息保证顺序传输,而分区之间则可以并发操作,这样就提高了Kafka的并发能力。
  • Kafka也支持在线增加分区,支持在线水平扩展。
  • Kafka支持为每个分区创建多个副本,其中只会有一个Leader副本负责读写,其他副本只负责与Leader副本进行同步,这种方式提高了数据的容灾能力。Kafka会将Leader副本均匀地分布在集群中的服务器上,实现性能最大化。
  • kafka利用顺序IO,以及page Cache达到的超高吞吐
2.7. zookeeper在Kafka中的作用?
  • Kafka使用ZooKeeper集群管理元数据,例如:记录Topic名称、分区以及其副本分配等信息,用户权限控制的相关数据等。Kafka 还会在ZooKeeper的某些上添加相应的监听器,用于监听集群的状态,例如:集群中所有Broker通过ZooKeeper监听Controller Leader的状态。
2.8. Kafka 中有哪些重要组件?
  • Broker——Kafka 服务器,负责各类 RPC 请求的处理以及消息的持久化。
  • 生产者——负责向 Kafka 集群生产消息。
  • 消费者——负责从 Kafka 集群消费消息。
  • 主题——保存消息的逻辑容器,生产者发送的每条消息都会被发送到某个主题上。

3. 分区

3.1. 主题分区的作用?
  • Kafka的每个Topic (主题)都可以分为多个Partition (分区),每个分区都有多个Replica(副本),实现消息冗余备份。每个分区中的消息是不同的,这类似于数据库中水平切分的思想,提高了并发读写的能力。 而同一分区的不同副本中保存的是相同的消息,副本之间是一主多从的关系,其中Leader副本负责处理读写请求,Follower 副本则只与Leader副本进行消息同步,当Leader副本出现故障时,则从Follower 副本中重新选举Leader副本对外提供服务。这样,通过提高分区的数量,就可以实现水平扩展,通过提高副本的数量,就可以提高容灾能力。
3.2. 分区规则
  • 分区规则
    • 如果指定了分区编号,用它
    • 如果没有指定分区号,但指定了key,按照hash计算分区号
    • 既没有分区号,也没有key,用 round-robin (轮询)
  • 默认分区存在问题
    • 通过key的hash计算分区号,存在hash冲突的可能
    • 如果后期增加分区,散列计算分区号,相同key将会落到和之前不一样的分区。
3.3. 分区过多有什么影响
  • 分区数的多少还会影响系统的可用性。如果分区数非常多,如果集群中的某个 broker 节点宕机,那么就会有大量的分区需要同时进行 leader 角色切换,这个切换的过程会耗费一笔可观的时间,并且在这个时间窗口内这些分区也会变得不可用。
  • 分区数越多也会让 Kafka 的正常启动和关闭的耗时变得越长,与此同时,主题的分区数越多不仅会增加日志清理的耗时,而且在被删除时也会耗费更多的时间。
3.4. 消费组中的消费者个数如果超过topic的分区,有什么影响,解决方案是什么?
  • 消费者的个数大于分区个数的情况,就会有消费者分配不到任何分区。
3.5. kafka是按照什么规则将消息划分到各个分区的?
  • 如果producer指定了要发送的目标分区,消息自然是去到那个分区;否则就按照producer端参数partitioner.class指定的分区策略来定;如果你没有指定过partitioner.class,那么默认的规则是:看消息是否有key,如果有则计算key的murmur2哈希值%topic分区数;如果没有key,按照轮询的方式确定分区。
3.6. 新增分区导致消息丢失、如何避免这种情况
  • 解释:新增加了分区之后consumer和producer不会立即感知,通常可能会等待一段时间。如果producer先感知到了并向新分区发送消息,那么consumer后感知到之后直接从最新位移开始读取消息,那么之前发送的消息就不会被消费了。
  1. 在业务场景允许暂停的的情况下,在增加主题分区前,先暂停Producer端的写入;然后增加主题分区;其次重启或等待Consumer端;最后启动Producer端.
  2. 在业务场景不允暂停的情况下,需要有个地方(redis/zookeeper)缓存一个配置信息.里面分别记录Producer端和Consumer端 主题分区信息. 比如,

Producer topic_partitions: M ,

Consumper topic_partitions: N

Producer端每次消费消息前,

首先,会判断Topic的分区数,如果有变更,会及时更新 Producer topic_partitions: M

其次,会判断 M 是否等于 N.

如果 M = N,则写入数据;

如果 M > N,则循环等待,不写入数据;

如果 M < N,则会更新配置文件(Producer topic_partitions:N),然后写入数据 Consumper端每次消费消息前,会判断Topic的分区数,如果有变更,会及时更新 Consumper topic_partitions: N

缺点,可能会增加一定的检测时长.是否增加此检测步骤后会影响到业务提交/消费需要根据业务特性进行压测检验.

0 人点赞