什么是 Kafka?
Apache Kafka 是一个开源的分布式流处理平台,最初由 LinkedIn 开发,后来成为 Apache 软件基金会的一部分。Kafka 主要用于构建实时数据管道和流处理应用程序。它能够高效地处理大量的数据流,广泛应用于日志收集、数据集成、实时分析等场景。
Kafka 的高效性源于以下几个关键设计理念和实现原理:
分布式架构:Kafka 采用分布式架构,能够横向扩展。通过增加 broker 的数量,可以轻松提高系统的吞吐量和可用性。
高吞吐量:Kafka 使用顺序写入和批量处理的方式来提高写入性能。消息被写入到磁盘时,Kafka 将多个消息合并为一个批次进行写入,减少了磁盘 I/O 操作。
持久化存储:Kafka 将消息持久化到磁盘,并使用高效的日志结构存储(Log-Structured Storage),确保数据的可靠性和持久性。
高可用性:Kafka 通过主副本机制和数据复制来确保高可用性。每个分区都有一个主副本和多个从副本,主副本负责处理读写请求,而从副本则保持与主副本的数据同步。
灵活的消费模式:Kafka 支持多种消费模式,包括点对点和发布/订阅模式,消费者可以根据需求选择适合的消费方式。
Kafka 的组件
组件 | 作用说明 | 类比现实案例 |
---|---|---|
Broker | 消息存储和转发的服务器节点 | 快递分拣中心 |
Topic | 消息的分类主题(逻辑概念) | 快递包裹上的地址标签 |
Partition | Topic的物理分片,保证并行处理 | 分拣中心的多个传送带 |
Producer | 消息生产者 | 寄件客户 |
Consumer | 消息消费者 | 收件客户 |
Consumer Group | 消费者组(实现并行消费) | 分拣中心的多个工作人员 |
1. Producer(生产者)
生产者是向 Kafka 发送消息的客户端应用程序。它负责将数据发布到指定的 Kafka topic。生产者可以选择目标 topic 和分区,并将消息序列化为字节流,负责处理消息的发送确认和错误处理。
2. Consumer(消费者)
消费者是从 Kafka 中读取消息的客户端应用程序。它可以订阅一个或多个 topic,并处理接收到的消息。消费者通过 offset 来跟踪已处理的消息。(订阅topic->拉取消息并处理->提交offset跟踪)
订阅topic:
使用 subscribe()方法:这是最常用的方式,消费者可以通过调用 subscribe() 方法来订阅一个或多个主题。KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("topic1", "topic2"));
使用assign()方法: 这种方式允许消费者手动分配特定的分区。通过 assign()
方法,消费者可以直接指定要消费的分区。例如:TopicPartition partition = new TopicPartition("topic1", 0); consumer.assign(Arrays.asList(partition));
消费模式
1、自动提交模式(Auto Commit):在自动提交模式下,消费者会定期自动提交消息的偏移量。Kafka 提供了一个配置项 enable.auto.commit,默认值为 true。在这种模式下,消费者在调用poll()方法后会自动提交已消费消息的偏移量。优点:实现简单,适合对消息处理顺序和可靠性要求不高的场景。缺点:可能会导致消息丢失或重复消费,因为在处理消息时,如果消费者崩溃,已提交的偏移量可能会指向未处理的消息。
2、手动提交模式(Manual Commit):在手动提交模式下,消费者需要显式地提交消息的偏移量消费者可以在处理完消息后,调用 commitSync() 或 commitAsync() 方法来提交偏移量。优点:提供了更高的控制能力,可以确保在消息处理成功后再提交偏移量,从而避免消息丢失和重复消费。缺点:实现相对复杂,需要开发者管理偏移量的提交逻辑。
3、消费组(Consumer Group):Kafka 支持消费者组的概念,多个消费者可以组成一个消费组共同消费同一个主题的消息。每个分区只能被同一个消费组中的一个消费者消费,这样可以实现负载均衡。优点:提高了系统的吞吐量和可扩展性,适合处理高并发的消息消费场景。缺点:需要注意消费者组的管理和协调,确保消费者的状态一致性。
3. Broker(代理)
broker 是 Kafka 集群中的服务器,负责存储和管理消息,接收来自生产者的消息并将其存储在分区中,响应消费者的请求,提供消息。处理数据的复制和故障转移。一个 Kafka 集群可以包含多个 broker,每个 broker 可以处理读写请求,并存储一部分数据。
4. Topic(主题)
主题是 Kafka 中消息的分类。每个主题可以有多个分区,消息根据主题进行组织。生产者将消息发送到特定的主题,消费者从主题中读取消息。作为消息的逻辑分组,生产者将消息发送到特定的主题。消费者从主题中读取消息。
5. Partition(分区)
每个主题可以被划分为多个分区,分区是 Kafka 中消息的基本存储单元。分区提供消息的有序存储,并支持并行处理,多个消费者可以同时从不同的分区读取消息。每个分区有一个主副本和多个从副本,确保数据的高可用性。
Replica:副本。一个topic的每个分区都有若干个副本,这些副本分布在不同的 Kafka Broker 上。每个分区的副本中,只有一个副本是领导者(Leader),其他副本是跟随者(Follower)。副本状态:ISR(In-Sync Replicas):Kafka 维护一个 ISR 列表,包含当前与领导者保持同步的所有副本。只有在 ISR 列表中的副本才能被视为有效的副本。副本的健康检查:Kafka 会定期检查副本的状态,如果某个跟随者未能及时同步数据,它将被移出 ISR 列表。
Leader:每个分区的领导者负责处理所有的读写请求,所有的生产者和消费者都与领导者进行交互,如果领导者副本发生故障,Kafka 会自动选举一个新的领导者,通常是 ISR 列表中的一个副本。这确保了系统的高可用性。
Follower:每个分区多个副本中的 "从",实时从 Leader 中同步数据,保持和 Leader 数据的同步,跟随者从领导者那里复制数据,保持与领导者的数据一致性。跟随者不直接处理客户端的请求。Leader 发生故障时,某个Follower会成为新的 Leader。
数据复制
数据写入:当生产者向主题的某个分区发送消息时,消息首先写入该分区的领导者副本。领导者会将消息追加到其日志中,并将消息同步到所有的跟随者副本。
同步复制:在领导者确认消息写入成功之前,要求所有的跟随者副本也必须成功写入。这种方式确保了数据的一致性,但可能会影响性能。
异步复制:领导者在写入成功后立即返回确认,而不等待跟随者的写入。这种方式提高了性能,但可能在领导者故障时导致数据丢失
6. Zookeeper
Zookeeper 是一个分布式协调服务,Kafka 使用 Zookeeper 来管理集群的元数据和状态。它维护 Kafka broker 的列表和状态,负责消费者组的协调和管理。处理分区的领导者选举和故障转移。
集群管理 | Zookeeper 负责管理 Kafka 集群的元数据,包括 Broker 的信息、主题、分区、消费者组等。它提供了一个集中式的存储和管理机制,使得 Kafka 集群能够高效地协调各个组件 |
领导者选举 | 在 Kafka 中,每个分区都有一个领导者副本和多个跟随者副本。Zookeeper 负责监控各个 Broker 的状态,并在领导者副本失效时进行领导者的选举。通过 Zookeeper,Kafka 能够快速地选举出新的领导者,确保系统的高可用性。 |
配置管理 | Zookeeper 存储 Kafka 的配置信息,包括主题的配置、分区副本因子、消费者组的偏移量等。通过 Zookeeper,Kafka 可以动态地更新和管理这些配置,而不需要重启 Broker |
监控和状态管理 | Zookeeper 提供了对 Kafka Broker 状态的监控功能。它可以检测 Broker 的上线和下线,并及时更新集群的状态信息。这使得 Kafka 能够在 Broker 发生故障时及时做出反应,保持集群的稳定性。 |
消费者组管理 | Zookeeper 还负责管理消费者组的信息,包括消费者的注册、偏移量的存储等。消费者在启动时会向 Zookeeper 注册,并获取分配的分区信息。这使得 Kafka 能够实现负载均衡和故障转移 |
7. Consumer Group(消费组)
消费者可以组成消费组,以便共享消息的消费。同一消费组中的消费者共享 offset,确保每条消息只被消费一次。提供负载均衡,多个消费者可以并行处理同一主题的不同分区。每个消费组都有一个唯一的标识符(Group ID)。提高吞吐量:消费组中的多个消费者可以并行消费同一主题的不同分区,从而提高消息处理的吞吐量。负载均衡:Kafka 会自动将主题的分区分配给消费组中的消费者,确保每个分区只被一个消费者消费,避免重复消费。独立性:不同的消费组可以独立消费同一主题的消息,每个消费组都有自己的消费进度和 offset。
8. Offset
Offset 是每个分区中消息的唯一标识符,表示消息在分区中的位置。消费者使用 offset 来跟踪已处理的消息,Kafka 将每个消费组的 offset 存储在内部的 __consumer_offsets topic 中。定位消息:消费者可以从指定的 Offset 开始消费消息。记录消费进度:消费者提交 Offset 到 Kafka,以便在重启后从上次消费的位置继续消费。保证消息顺序:Offset 保证了分区内消息的有序性。
Kafka 如何保证消息顺序性?
Kafka 保证消息顺序性的机制主要依赖于其分区(Partition)设计和消息的写入方式。
1、分区的顺序性
在 Kafka 中,每个主题可以被划分为多个分区。每个分区是一个有序的消息队列,Kafka 保证同一个分区内的消息是严格有序的。这意味着:消息在写入到某个分区时,Kafka 会按照写入的顺序将其存储在分区的日志中。消费者从分区中读取消息时,消息的顺序与写入顺序一致。
2、消息的分区策略
为了保证消息的顺序性,生产者在发送消息时可以选择特定的分区策略:基于键的分区:如果生产者在发送消息时指定了一个键(Key),Kafka 会根据该键的哈希值将消息发送到特定的分区。这样,具有相同键的消息会被发送到同一个分区,从而保证这些消息的顺序性。轮询或随机分区:如果不指定键,Kafka 会使用轮询或随机的方式将消息分配到不同的分区,这样可能会导致消息顺序的打乱。因此,在需要保证顺序的场景中,建议使用基于键的分区策略。
3、消费者的顺序性
消费者在消费消息时,通常会从一个分区中顺序读取消息。Kafka 的消费者在处理消息时,应该确保:每个消费者实例只处理一个分区的消息,这样可以保证消息的顺序性。如果一个消费者组中的多个消费者实例同时消费多个分区,消息的顺序性可能会受到影响。因此,在需要顺序消费的场景中,通常会将每个消费者实例与一个分区一一对应。
顺序性必须的场景如 1. 金融交易(转账操作必须按照发生的顺序进行处理)2. 订单处理(如下单、支付、发货、完成)需要按照顺序进行处理。3. 日志记录:日志的顺序反映了事件发生的时间顺序。如果日志记录的顺序混乱,可能会导致后续的故障排查和分析变得困难。4. 实时数据处理;5. 状态机(状态机的状态转移依赖于事件的顺序。如果事件的顺序被打乱,可能导致状态机进入错误的状态)
kafka消息持久化
Kafka 消息持久化机制
1、日志结构存储(Log-Structured Storage)
Kafka 使用日志结构存储(Log-Structured Storage)来持久化消息。每个分区对应一个日志文件,消息以追加的方式写入到日志中。具体过程如下:顺序写入:Kafka 将消息追加到日志文件的末尾,这种顺序写入的方式极大地提高了写入性能,因为它减少了随机 I/O 操作。
分段文件:每个分区的日志文件被划分为多个段(Segment),每个段文件的大小是可配置的(默认是 1GB)。当一个段文件达到指定大小后,Kafka 会创建一个新的段文件继续写入。
索引文件:为了提高消息的读取效率,Kafka 为每个段文件维护一个索引文件。索引文件记录了每条消息在日志文件中的偏移量(offset),使得消费者可以快速定位到特定的消息。
2、消息的持久化流程
生产者发送消息:生产者将消息发送到 Kafka 的某个主题(topic)和分区(partition)。
消息序列化:消息在发送之前会被序列化为字节流。
写入日志:Kafka broker 接收到消息后,将其追加到对应分区的日志文件中。
写入索引:同时,Kafka 会更新索引文件,记录新消息的偏移量。
确认持久化:根据生产者的配置,Kafka 可以选择在消息写入日志后立即确认(ack=1),或者等待所有副本都写入成功后再确认(ack=all)。
3、数据复制
为了确保数据的高可用性,主副本机制:每个分区都有一个主副本(Leader)和多个从副本(Follower)。所有的读写请求都由主副本处理,而从副本则负责复制主副本的数据。
异步复制:从副本通常以异步方式从主副本获取数据,这样可以提高写入性能。
数据一致性:Kafka 提供了配置选项,允许生产者在发送消息时选择等待所有副本确认(ack=all),以确保数据的一致性。
4、数据保留策略
Kafka 还提供了灵活的数据保留策略,以控制消息的存储时间和空间:
基于时间的保留:可以设置消息的保留时间(如 7 天),超过这个时间的消息将被删除。
基于大小的保留:可以设置每个分区的最大存储空间,超过这个空间的消息将被删除。
手动删除:用户可以手动删除特定的主题或分区中的消息。
默认存储机制
日志结构存储:使用顺序写入的方式将消息持久化到磁盘。
分段文件:将每个分区的日志文件划分为多个段文件,以便于管理和读取。
索引文件:为每个段文件维护索引,以提高消息的读取效率。
数据复制:通过主副本和从副本机制确保数据的高可用性。
Kafka 的生产者(Producer)消息发送
Kafka 的生产者(Producer)在发送消息时采用了一系列机制来确保高效和可靠的数据传输。通过批量发送消息,生产者可以显著提高吞吐量。
Kafka 生产者发送消息的流程
1、创建生产者:在使用 Kafka 之前,首先需要创建一个生产者实例。生产者配置包括 Kafka Broker 的地址、序列化器、ACKs 设置等。
2、选择分区:当生产者发送消息时,它需要确定将消息发送到哪个分区。Kafka 提供了以下几种分区选择策略:轮询(Round Robin):将消息均匀分配到各个分区。
基于键的分区:如果消息包含一个键,生产者会使用该键的哈希值来确定分区。这种方式可以确保同一键的消息总是发送到同一个分区,从而保持顺序性。
3、发送消息:生产者通过调用 send() 方法将消息发送到指定的主题和分区。此时,消息会被放入一个发送缓冲区。
4、异步发送:Kafka 生产者的 send() 方法是异步的,调用该方法后,生产者不会阻塞等待消息的确认,而是立即返回。生产者会在后台线程中处理消息的发送和确认。
5、确认与重试:生产者会根据 ACKs 配置等待相应的确认。如果消息发送失败,生产者会根据配置的重试策略进行重试。ACKs 是 Kafka 生产者在发送消息时的确认机制,指的是生产者在发送消息后,等待来自 Kafka Broker 的确认的方式。ACKs 的配置决定了生产者在消息发送成功之前需要等待多少个副本的确认。acks=0:生产者发送消息后,不等待任何确认,消息发送后立即返回成功。acks=1:生产者发送消息后,等待领导者副本的确认。只要领导者成功写入消息,生产者就会收到确认。acks=all(或 acks=-1):生产者发送消息后,等待所有 ISR(In-Sync Replicas)中的副本的确认。只有当所有副本都成功写入消息后,生产者才会收到确认。(值得不同影响数据可靠性与性能)
通过批量发送提高吞吐量
1、批量发送配置:
batch.size:指定单个批次的最大字节数。生产者会在达到该字节数时发送批次。
linger.ms:指定生产者在发送批次前等待的时间。如果在这个时间内没有达到 batch.size,生产者会发送当前的批次。这个参数可以用来增加批次的大小,从而提高吞吐量。
buffer.memory:指定生产者用于缓冲消息的内存大小。如果缓冲区满,生产者会阻塞或抛出异常
2、批量发送的优势
减少网络往返:通过将多条消息打包成一个批次发送,生产者可以减少与 Kafka Broker 之间的网络往返次数,从而提高吞吐量。
提高资源利用率:批量发送可以更有效地利用网络带宽和 Broker 的处理能力,降低每条消息的传输开销。
降低延迟:通过合理配置 linger.ms,生产者可以在一定时间内等待更多消息的到来,从而形成更大的批次,进一步降低延迟。
Exactly Once的实现
Kafka 提供了“Exactly Once”语义(EOS,精确一次语义)的保证,确保消息在生产和消费过程中只被处理一次。这一特性对于需要高可靠性和一致性的应用场景至关重要。
实现依赖机制:
1、幂等生产者(Idempotent Producer)
幂等生产者确保同一条消息在多次发送时只会被写入一次。Kafka 通过为每个生产者分配一个唯一的 ID(Producer ID)和每条消息分配一个序列号来实现这一点。Producer ID:每个生产者在连接到 Kafka 时会被分配一个唯一的 ID。序列号:每个生产者发送的消息都有一个序列号,Kafka 会根据 Producer ID 和序列号来判断消息是否是重复的。
当生产者发送消息时,Kafka 会检查消息的 Producer ID 和序列号,如果发现重复的消息(即相同的 Producer ID 和序列号),则会忽略该消息,从而实现幂等性。
2、事务支持(Transactional Support)
Kafka 引入了事务机制,使得生产者可以在一个事务中发送多条消息。通过事务,生产者可以确保一组消息要么全部成功发送,要么全部不发送。
3、消费者端的处理
在消费者端,Kafka 通过管理偏移量来确保消息的精确处理。消费者可以在处理完消息后手动提交偏移量,确保只有在成功处理消息后才会更新偏移量。
手动提交偏移量:消费者在处理完消息后调用 commitSync() 或 commitAsync() 方法提交偏移量,确保消息只被处理一次。
事务性消费者:消费者也可以使用事务性消费,确保在处理消息时与生产者的事务一致。