一、什么是消息中间件?

消息中间件是基于队列与消息传递技术,在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统。

消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。

二、kafka 是什么?有什么作用?

Kafka 是一个分布式的流式处理平台,它以高吞吐、可持久化、可水平扩展、支持流数据处理等多种特性而被广泛使用。

主要功能体现于三点:

  1. 消息系统:kafka与传统的消息中间件都具备系统解耦、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等功能。与此同时,kafka还提供了大多数消息系统难以实现的消息顺序性保障及回溯性消费的功能。

  2. 存储系统:kafka把消息持久化到磁盘,相比于其他基于内存存储的系统而言,有效的降低了消息丢失的风险。这得益于其消息持久化和多副本机制。也可以将kafka作为长期的存储系统来使用,只需要把对应的数据保留策略设置为“永久”或启用主题日志压缩功能。

  3. 流式处理平台:kafka为流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理框架,比如窗口、连接、变换和聚合等各类操作。

三、kafka 的架构是怎么样的?

一个典型的 kafka 体系架构包括若干 Producer、若干 Consumer、以及一个 Zookeeper 集群(在2.8.0版本中移,除了 Zookeeper,通过 KRaft 进行自己的集群管理)

Producer 将消息发送到 Broker,Broker 负责将受到的消息存储到磁盘中,而 Consumer 负责从 Broker 订阅并消费消息。

Kafka 基本概念

  • Producer(生产者):

    • 生产者是向 Kafka 发布消息的应用程序或服务。
    • 生产者将消息发送到特定的 topic。
  • Consumer(消费者):

    • 消费者是从 Kafka 中读取消息的应用程序或服务。
    • 消费者订阅 topic 并处理其中的消息。
  • Consumer Group(消费者组):

    • 消费者组是一组可以协同工作的消费者。
    • 每个消费者组内的消费者会分配不同的分区进行消费,从而实现并行处理。
    • 如果一个 topic 有多个分区,消费者组内的消费者可以同时消费这些分区,提高消费效率。
  • Broker:

    • Kafka 集群由多个 broker 组成,它们是 Kafka 的服务器节点。
    • broker 负责存储和转发消息。
  • Topic:

    • topic 是 Kafka 中消息分类的逻辑容器。
    • 每个 topic 可以有多个 partition,以便于水平扩展。
  • Partition:

    • 分区是 topic 的物理分割,每个分区都是一个有序的消息队列。
    • 分区可以分布在不同的 broker 上,这有助于负载均衡和数据冗余。
  • Replica(副本):

    • Kafka 使用副本机制来确保数据的高可用性和容错性。
    • 每个分区有多个副本,包括一个 leader 和多个 follower。
  • Leader:

    • leader 是分区的主要副本,负责处理所有读写请求。
    • 当 leader 失效时,一个 follower 将被选为新的 leader。
  • Follower:

    • follower 是分区的辅助副本,它们从 leader 同步数据。
    • follower 的存在是为了提供数据冗余,以防数据丢失。

四、Kafka Replicas是怎么管理的?

  1. AR:分区中的所有 Replica 统称为 AR

  2. ISR:所有与 Leader 副本保持一定程度同步的Replica(包括 Leader 副本在内)组成 ISR

  3. OSR:与 Leader 副本同步滞后过多的 Replica 组成了 OSR

Leader 负责维护和跟踪 ISR 集合中所有 Follower 副本的滞后状态,当 Follower 副本落后过多时,就会将其放入 OSR 集合,当 Follower 副本追上了 Leader 的进度时,就会将其放入 ISR 集合。

默认情况下,只有 ISR 中的副本才有资格晋升为 Leader

五、如何确定当前能读到哪一条消息?

Apache Kafka 确定消费者能够读取到哪条消息的过程涉及多个关键概念,包括分区、偏移量(offset)、消费者组以及提交机制。下面是具体说明:

  1. 分区(Partition): Kafka 将每个主题(topic)的消息划分为多个分区。每个分区是一个有序的、不可变的消息队列。消息在分区中的顺序是由生产者发送时指定的键(key)决定的,或者如果没有键,则按发布顺序排序。

  2. 偏移量(Offset): 每条消息在分区中都有一个唯一的偏移量,这个偏移量是该消息在分区内的位置标识。消费者通过指定偏移量来读取消息。

  3. 消费者组(Consumer Group): Kafka 中的消费者属于不同的消费者组。同一组内的消费者会互相竞争消息,而不同组的消费者则各自独立消费。一个分区的消息只会被一个消费者组中的一个消费者消费。

  4. 位移提交(Offset Commit): Kafka 允许消费者控制他们消费到哪个偏移量,并可以周期性地提交(commit)这个偏移量到 Kafka 的一个特殊的主题(__consumer_offsets)中。这决定了消费者下次启动时从哪里继续消费。消费者可以选择自动提交偏移量,也可以手动控制。

确定当前能读到哪条消息的过程如下:

  1. 首次消费或重启:

    • 如果消费者是第一次读取一个主题,或者是在组内还没有偏移量提交记录,它可以配置为从最早或最新的消息开始消费。
    • 通过设置 auto.offset.reset 配置项为 earliest 或 latest 来实现。earliest 表示从分区的最开始消费,latest 则是从最新未消费的消息开始。
  2. 正常消费:

    • 当消费者开始从一个主题消费时,它会从上次提交的偏移量处开始读取。Kafka 会根据消费者的位移提交记录,将消费者定位到正确的消息位置。
  3. 位移管理:

    • 消费者可以根据业务需求,选择合适的偏移量提交策略。自动提交可以在消费完一定数量的消息或经过一定时间后触发;手动提交则给予应用程序更多控制,决定何时认为消息已经被安全处理。

总之,Kafka 通过维护每个消费者组在每个分区上的偏移量,并允许消费者根据提交的偏移量来决定从哪个位置开始读取消息,以此确保消息的有序消费及不丢失。

六、Producer生产者发送消息有哪些模式?

总共有三种模式:

  1. 发后即忘(fire-and-forget)
    它只管往 Kafka 里面发送消息,但是不关心消息是否正确到达,这种方式的效率最高,但是可靠性也最差,比如当发生某些不可充实异常的时候会造成消息的丢失。

  2. 同步(sync)
    producer.send()返回一个Future对象,调用get()方法进行同步等待,就知道消息是否发送成功,发送一条消息需要等上个消息发送成功后才可以继续发送。

  3. 异步(async)
    Kafka支持 producer.send() 传入一个回调函数,消息不管成功或者失败都会调用这个回调函数,这样就算是异步发送,我们也知道消息的发送情况,然后在回调函数中选择记录日志还是重试都取决于调用方。

七、发送消息的分区策略有哪些?

  1. 轮询:依次将消息发送该topic下的所有分区,如果在创建消息的时候 key 为 null,Kafka 默认采用这种策略。

  2. key 指定分区:在创建消息是 key 不为空,并且使用默认分区器,Kafka 会将 key 进行 hash,然后根据hash值映射到指定的分区上。这样的好处是 key 相同的消息会在一个分区下,Kafka 并不能保证全局有序,但是在每个分区下的消息是有序的,按照顺序存储,按照顺序消费。在保证同一个 key 的消息是有序的,这样基本能满足消息的顺序性的需求。但是如果 partation 数量发生变化,那就很难保证 key 与分区之间的映射关系了。

  3. 自定义策略:实现 Partitioner 接口就能自定义分区策略。

  4. 指定 Partiton 发送

八、Kafka 支持读写分离吗?为什么?

Kafka 的设计并不直接支持读写分离,原因在于:

  1. 数据延迟敏感:Kafka 承担的操作型业务对数据延迟非常敏感,主从读写分离会导致数据延迟增加,因为数据需要从主节点复制到从节点才能被读取。

  2. 半同步复制:Kafka 的半同步复制机制保证了一定程度的数据一致性和可靠性,但如果采用读写分离,从节点的数据可能不是最新的,这与 Kafka 的设计目标不符。

  3. 高可用性:Kafka 的高可用性是通过多副本机制实现的,每个分区的多个副本分布在不同的 broker 上,这样即使某个 broker 失效,其他副本仍然可以提供服务。

九、Kafka 是怎么去实现负载均衡的?

Apache Kafka 实现负载均衡主要体现在两个方面:生产者负载均衡消费者负载均衡

生产者负载均衡

Kafka 的生产者负载均衡主要是通过消息分区(Partitioning)来实现的。当生产者发送消息到一个特定的主题(Topic)时,消息会被分配到该主题的不同分区中。Kafka 提供了多种分区策略:

  1. 默认分区器(DefaultPartitioner):

    • 当消息没有显式指定分区键(Key)时,Kafka 使用轮询(Round Robin)策略将消息均匀分布到主题的所有分区中。
    • 当消息携带了分区键时,Kafka 会对键进行哈希运算,然后根据哈希值确定消息应该被发送到哪个分区。这样可以确保具有相同键的消息会被发送到相同的分区,这对于需要按键聚合数据的场景很有帮助。
  2. 自定义分区器

    • 开发者还可以实现自定义的分区器,以适应特定的负载均衡需求,比如基于消息内容或元数据进行更精细的分区控制。

消费者负载均衡
Kafka 的消费者负载均衡是通过消费者组(Consumer Group)和再均衡(Rebalancing)机制来实现的。

  1. 消费者组
    每个消费者属于一个消费者组,同一组内的消费者会竞争消费同一主题的分区。这意味着一个主题的分区在任何时候只会被同一组内的一个消费者消费
    消费者组允许将一个主题的分区负载均衡地分配给组内的多个消费者,从而实现并行消费和负载均衡。

  2. 再均衡(Rebalancing):
    当消费者组中的消费者数量发生变化时(例如,新消费者加入或现有消费者离开),Kafka 会触发再均衡过程,重新分配主题的分区给当前组内的消费者。
    再均衡过程确保了每个消费者都能获得大致相等的分区数量,从而达到负载均衡的目的。

  3. 分区分配策略
    Kafka 提供了多种分区分配策略,如范围分配(Range Assignment)、循环分配(Round Robin Assignment)等,这些策略可以进一步优化负载均衡。

十、Kafka 的负责均衡会有什么问题呢?

kafka的负载均衡在绝对理想的状况下可以实现,但是会有某些情况出现一定程度上的负载不均衡
在这里插入图片描述

  1. broker 端分配不均:当创建 topic 的时候可能会出现某些 broker 分配到的分区数多,而有些 broker 分配的分区少,这就导致了 leader 多副本不均。

  2. 生产者写入消息不均:生产者可能只对某些 broker 中的 leader 副本进行大量的写入操作,而对其他的 leader 副本不闻不问。

  3. 消费者消费不均:消费者可能只对某些 broker 中的 leader 副本进行大量的拉取操作,而对其他的 leader 副本不闻不问。

  4. leader 副本切换不均:当主从副本切换或者分区副本进行了重分配后,可能会导致各个 broker 中的 leader 副本分配不均匀。

十一、Kafka 的可靠性是怎么保证的?

  1. acks
    这个参数用来指定分区中有多少个副本收到这条消息,生产者才认为这条消息是写入成功的,这个参数有三个值:

    • acks = 1,默认为1。生产者发送消息,只要 leader 副本成功写入消息,就代表成功。这种方案的问题在于,当返回成功后,如果 leader 副本和 follower 副本还没有来得及同步,leader 就崩溃了,那么在选举后新的 leader 就没有这条消息,也就丢失了。
    • acks = 0。生产者发送消息后直接算写入成功,不需要等待响应。这个方案的问题很明显,只要服务端写消息时出现任何问题,都会导致消息丢失。
    • acks = -1 或 acks = all。生产者发送消息后,需要等待 ISR 中的所有副本都成功写入消息后才能收到服务端的响应。毫无疑问这种方案的可靠性是最高的,但是如果 ISR 中只有leader 副本,那么就和 acks = 1 毫无差别了。
  2. 消息发送的方式

第6问中我们提到了生产者发送消息有三种方式,发完即忘,同步和异步。我们可以通过同步或者异步获取响应结果,失败做重试来保证消息的可靠性。

  1. 手动提交位移

默认情况下,当消费者消费到消息后,就会自动提交位移。但是如果消费者消费出错,没有进入真正的业务处理,那么就可能会导致这条消息消费失败,从而丢失。我们可以开启手动提交位移,等待业务正常处理完成后,再提交offset。

十二、Kafka 的消息消费方式有哪些?

一般消息消费有两种模式,推和拉。Kafka的消费是属于拉模式的,而此模式的消息消费方式有两种,点对点发布订阅

在这里插入图片描述

  1. 点对点:如果所有消费者属于同一个消费组,那么所有的消息都会被均匀的投递给每一个消费者,每条消息只会被其中一个消费者消费。
  2. 发布订阅:如果所有消费者属于不同的消费组,那么所有的消息都会被投递给每一个消费者,每个消费者都会收到该消息。

十三、分区再分配是做什么的?解决了什么问题?

分区再分配主要是用来维护 kafka 集群的负载均衡

既然是分区再分配,那么 kafka 分区有什么问题呢?

问题1:当集群中的一个节点下线了

  • 如果该节点的分区是单副本的,那么分区将会变得不可用
  • 如果是多副本的,就会进行 leader 选举,在其他机器上选举出新的 leader
    kafka 并不会将这些失效的分区迁移到其他可用的 broker 上,这样就会影响集群的负载均衡,甚至也会影响服务的可靠性和可用性

问题2:当集群新增 broker 时,只有新的主题分区会分配在该 broker 上,而老的主题分区不会分配在该 broker 上,就造成了老节点和新节点之间的负载不均衡。

  • 为了解决该问题就出现了分区再分配,它可以在集群扩容,broker 失效的场景下进行分区迁移。

分区再分配的原理就是通过控制器给分区新增新的副本,然后通过网络把旧的副本数据复制到新的副本上,在复制完成后,将旧副本清除。 当然,为了不影响集群正常的性能,在此复制期间还会有一些列保证性能的操作,比如复制限流。

十四、副本 leader 是怎么选举的?

当分区 leader 节点崩溃时,其中一个 follower 节点会成为新的 leader 节点,这样会导致集群的负载不均衡,从而影响服务的健壮性和稳定性。

Topic: test Partation:0 Leader:1 Replicas:1,2,0 Isr:1,2,0
Topic: test Partation:1 Leader:2 Replicas:2,0,1 Isr:2,0,1
Topic: test Partation:2 Leader:0 Replicas:0,1,2 Isr:0,1,2

我们可以看到

0 分区有 1 个 leader
1 分区有 2 个 leader
2 分区有 0 个 leader

如果此时中间的节点重启

Topic: test Partation:0 Leader:1 Replicas:1,2,0 Isr:1,0,2
Topic: test Partation:1 Leader:0 Replicas:2,0,1 Isr:0,1,2
Topic: test Partation:2 Leader:0 Replicas:0,1,2 Isr:0,1,2

我们又可以看到:

0 分区有 1 个 leader
1 分区有 0 个 leader
2 分区有 0 个 leader
我们会发现,原本 1 分区有两个 ledaer,经过重启后 leader 都消失了,如此就负载不均衡了。

为了解决这种问题,就引入了优先副本的概念

优先副本就是 AR 集合中的第一个副本。比如分区 2 的 AR 为 0,1,2,那么分区 2 的优先副本就为0。理想情况下优先副本就是 leader 副本。优先副本选举就是促使优先副本成为 leader 副本,从而维护集群的负载均衡。

十五、分区数越多越好吗?吞吐量就会越高吗?

一般类似于这种问题的答案,都是持否定态度的。

但是可以说,在一定条件下,分区数的数量是和吞吐量成正比的,分区数和性能也是成正比的。

那么为什么说超过了一定限度,就会对性能造成影响呢?原因如下:

  1. 客户端/服务器端需要使用的内存就越多

服务端在很多组件中都维护了分区级别的缓存,分区数越大,缓存成本也就越大。
消费端的消费线程数是和分区数挂钩的,分区数越大消费线程数也就越多,线程的开销成本也就越大
生产者发送消息有缓存的概念,会为每个分区缓存消息,当积累到一定程度或者时间时会将消息发送到分区,分区越多,这部分的缓存也就越大

  1. 文件句柄的开销
    每个 partition 都会对应磁盘文件系统的一个目录。在 Kafka 的数据日志文件目录中,每个日志数据段都会分配两个文件,一个索引文件和一个数据文件。每个 broker 会为每个日志段文件打开一个 index 文件句柄和一个数据文件句柄。因此,随着 partition 的增多,所需要保持打开状态的文件句柄数也就越多,最终可能超过底层操作系统配置的文件句柄数量限制。

  2. 越多的分区可能增加端对端的延迟
    Kafka 会将分区 HW 之前的消息暴露给消费者。分区越多则副本之间的同步数量就越多,在默认情况下,每个 broker 从其他 broker 节点进行数据副本复制时,该 broker 节点只会为此工作分配一个线程,该线程需要完成该 broker 所有 partition 数据的复制。

  3. 降低高可用性
    在第 13 问我们提到了分区再分配,会将数据复制到另一份副本当中,分区数量越多,那么恢复时间也就越长,而如果发生宕机的 broker 恰好是 controller 节点时:在这种情况下,新 leader 节点的选举过程在 controller 节点恢复到新的 broker 之前不会启动。controller 节点的错误恢复将会自动地进行,但是新的 controller 节点需要从 zookeeper 中读取每一个 partition 的元数据信息用于初始化数据。例如,假设一个Kafka 集群存在 10000个partition,从 zookeeper 中恢复元数据时每个 partition 大约花费 2 ms,则 controller 的恢复将会增加约 20 秒的不可用时间窗口。

十六、如何增强消费者的消费能力?

  1. 可以考虑增加 topic 的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。

  2. 如果是消费者消费不及时,可以采用多线程的方式进行消费,并且优化业务方法流程,同样的分区数,为什么人家并发那么高,你的就不行??

十七、kafka 控制器是什么?有什么作用

在 Kafka 集群中会有一个或多个 broker,其中有一个 broker 会被选举为控制器,它负责管理整个集群中所有分区和副本的状态,kafka 集群中只能有一个控制器

当某个分区的 leader 副本出现故障时,由控制器负责为该分区选举新的 leader 副本

当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有 broker 更新其元数据信息

当为某个 topic 增加分区数量时,由控制器负责分区的重新分配

十八、kafka 控制器是怎么进行选举的?

kafka控制器的选举机制依赖于ZooKeeper,以下是控制器选举的基本流程:

  1. 初始化状态: 当Kafka集群启动时,每个Broker都会启动一个KafkaController实例。这些实例将尝试在ZooKeeper中注册自己为控制器。

  2. ZooKeeper选举: 每个Broker的KafkaController都会尝试在ZooKeeper中的特定节点上创建一个临时的、顺序的节点。ZooKeeper保证了创建最小编号节点的Broker将成为控制器。这是因为ZooKeeper的原子性,确保了只有一个Broker能成功创建这个节点。

  3. 成为控制器: 成功创建节点的Broker被选为控制器。它将开始监听ZooKeeper上的相关事件,如Broker的加入或离开、分区领导者的变更等。

  4. 控制器职责: 一旦被选为控制器,该Broker将负责处理所有元数据变更请求,例如当一个Broker失败时,它会重新选举分区领导者,更新ISR列表,以及处理任何其他元数据变更。

  5. 故障转移: 如果当前的控制器Broker失败,ZooKeeper将触发一次新的选举。所有剩余的Broker将再次尝试创建临时节点,最小编号的节点创建者将成为新的控制器。

  6. 心跳机制: 为了检测控制器是否仍然活跃,控制器会定期向ZooKeeper发送心跳。如果ZooKeeper在一定时间内没有收到控制器的心跳,它将认为控制器已失效,并触发新的选举。

  7. 选举后处理: 新的控制器将接管前一个控制器的所有职责,并继续监听和处理ZooKeeper上的事件。

这个选举机制确保了在任何时候,Kafka集群中只有一个活动的控制器,从而避免了元数据变更冲突的问题。同时,由于选举是在ZooKeeper中进行的,这保证了选举过程的可靠性和一致性。

十九、kafka 为什么这么快?

  1. 顺序读写
    磁盘分为顺序读写与随机读写,基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,kafka 这里采用的就是顺序读写。

  2. Page Cache
    为了优化读写性能,Kafka 利用了操作系统本身的 Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。

  3. 零拷贝
    Kafka使用了零拷贝技术,也就是直接将数据从内核空间的读缓冲区直接拷贝到内核空间的 socket 缓冲区,然后再写入到 NIC 缓冲区,避免了在内核空间和用户空间之间穿梭。

  4. 分区分段+索引
    Kafka 的 message 是按 topic分 类存储的,topic 中的数据又是按照一个一个的 partition 即分区存储到不同 broker 节点。每个 partition 对应了操作系统上的一个文件夹,partition 实际上又是按照segment分段存储的。
    通过这种分区分段的设计,Kafka 的 message 消息实际上是分布式存储在一个一个小的 segment 中的,每次文件操作也是直接操作的 segment。为了进一步的查询优化,Kafka 又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。

  5. 批量读写
    Kafka 数据读写也是批量的而不是单条的,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。

  6. 批量压缩
    Kafka 把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络 IO 损耗,通过 mmap 提高 I/O 速度,写入数据的时候由于单个Partion是末尾添加所以速度最优;读取数据的时候配合 sendfile 进行直接读取。

二十、什么情况下 kafka 会丢失消息?

Kafka 有三次消息传递的过程:生产者发消息给 Broker,Broker 同步消息和持久化消息,Broker 将消息传递给消费者。这其中每一步都有可能丢失消息.

  1. 生产者发送数据: 在第 11 问中的 acks中有说到
    当 acks 为 0,只要服务端写消息时出现任何问题,都会导致消息丢失。
    当 acks 配置为 1 时,生产者发送消息,只要 leader 副本成功写入消息,就代表成功。这种方案的问题在于,当返回成功后,如果 leader 副本和 follower 副本还没有来得及同步,leader 就崩溃了,那么在选举后新的 leader 就没有这条消息,也就丢失了。

  2. Broker 存储数据:kafka 通过 Page Cache 将数据写入磁盘。
    Page Cache 就是当往磁盘文件写入的时候,系统会先将数据流写入缓存中,但是什么时候将缓存的数据写入文件中是由操作系统自行决定。所以如果此时机器突然挂了,也是会丢失消息的。

  3. 消费者消费数据:在开启自动提交 offset 时,只要消费者消费到消息,那么就会自动提交偏移量,如果业务还没有来得及处理,那么消息就会丢失。

Logo

Kafka开源项目指南提供详尽教程,助开发者掌握其架构、配置和使用,实现高效数据流管理和实时处理。它高性能、可扩展,适合日志收集和实时数据处理,通过持久化保障数据安全,是企业大数据生态系统的核心。

更多推荐