Kafka原理浅析

1.Kafka简介       

     Kafka 是一个消息系统,原本开发自 LinkedIn,用作 LinkedIn 的活动流(Activity Stream)和运营数据处理管道(Pipeline)的基础。现在它已被多家公司作为多种类型的数据管道和消息系统使用。活动流数据是几乎所有站点在对其网站使用情况做报表时都要用到的数据中最常规的部分。活动数据包括页面访问量(Page View)、被查看内容方面的信息以及搜索情况等内容。这种数据通常的处理方式是先把各种活动以日志的形式写入某种文件,然后周期性地对这些文件进行统计分析。运营数据指的是服务器的性能数据(CPU、IO 使用率、请求时间、服务日志等等数据),总的来说,运营数据的统计方法种类繁多。

2.Kafka基本架构

Broker:Kafka 集群包含一个或多个服务器,这种服务器被称为 broker。

Topic:每条发布到 Kafka 集群的消息都有一个类别,这个类别被称为 Topic。(物理上不同 Topic 的消息分开存储,逻辑上一个 Topic 的消息虽然保存于一个或多个 broker 上,但用户只需指定消息的 Topic 即可生产或消费数据而不必关心数据存于何处)。

Partition:Partition 是物理上的概念,每个 Topic 包含一个或多个 Partition。

Producer:负责发布消息到 Kafka broker。

Consumer:消息消费者,向 Kafka broker 读取消息的客户端。

Consumer Group:每个 Consumer 属于一个特定的 Consumer Group(可为每个 Consumer 指定 group name,若不指定 group name 则属于默认的 group)。

3.Kafka交互流程  

       Kafka 是一个基于分布式的消息发布-订阅系统,它被设计成快速、可扩展的、持久的。与其他消息发布-订阅系统类似,Kafka 在主题当中保存消息的信息。生产者向主题写入数据,消费者从主题读取数据。由于 Kafka 的特性是支持分布式,同时也是基于分布式的,所以主题也是可以在多个节点上被分区和覆盖的。

       信息是一个字节数组,程序员可以在这些字节数组中存储任何对象,支持的数据格式包括 String、JSON、Avro。Kafka 通过给每一个消息绑定一个键值的方式来保证生产者可以把所有的消息发送到指定位置。属于某一个消费者群组的消费者订阅了一个主题,通过该订阅消费者可以跨节点地接收所有与该主题相关的消息,每一个消息只会发送给群组中的一个消费者,所有拥有相同键值的消息都会被确保发给这一个消费者。

      Kafka 设计中将每一个主题分区当作一个具有顺序排列的日志。同处于一个分区中的消息都被设置了一个唯一的偏移量。Kafka 只会保持跟踪未读消息,一旦消息被置为已读状态,Kafka 就不会再去管理它了。Kafka 的生产者负责在消息队列中对生产出来的消息保证一定时间的占有,消费者负责追踪每一个主题 (可以理解为一个日志通道) 的消息并及时获取它们。基于这样的设计,Kafka 可以在消息队列中保存大量的开销很小的数据,并且支持大量的消费者订阅。

4.Kafka在zk中的存储结构 

 kafka 在 zookeeper 中的存储结构如下图所示:


  • topic注册信息

  /brokers/topics/[topic] :存储某个topic的partitions所有分配信息

  • partition状态信息

   /brokers/topics/[topic]/partitions/[0...N]  其中[0..N]表示partition索引号

   /brokers/topics/[topic]/partitions/[partitionId]/state

  • Broker注册信息

  /brokers/ids/[0...N]:每个broker的配置文件中都需要指定一个数字类型的id(全局不可重复),此节点为临时znode(EPHEMERAL)

  • Controller epoch

    /controller_epoch -> int (epoch) :此值为一个数字,kafka集群中第一个broker第一次启动时为1,以后只要集群中center controller中央控制器所在broker变更或挂掉,就会重新选举新的center controller,每次center controller变更controller_epoch值就会 + 1

  • Controller注册信息

   /controller -> int (broker id of the controller)  存储center controller中央控制器所在kafka broker的信息

5.topic 

       kafka将所有消息组织成多个topic的形式存储,而每个topic又可以拆分成多个partition,每个partition又由一个一个消息组成。每个消息都被标识了一个递增序列号代表其进来的先后顺序,并按顺序存储在partition中。


这样,消息就以一个个id的方式,组织起来。

      producer选择一个topic,生产消息,消息会通过分配策略append到某个partition末尾

      consumer选择一个topic,通过id指定从哪个位置开始消费消息。消费完成之后保留id,下次可以从这个位置开始继续消费,也可以从其他任意位置开始消费。这个id,在kafka中被称为offset。

6.consumer 
  • Consumer Group 与 topic 订阅

       每个Consumer 进程都会划归到一个逻辑的Consumer Group中,逻辑的订阅者是Consumer Group。所以一条message可以被多个订阅message 所在的topic的每一个Consumer Group,也就好像是这条message被广播到每个Consumer Group一样。而每个Consumer Group中,类似于一个Queue(JMS中的Queue)的概念差不多,即一条消息只会被Consumer Group中的一个Consumer消费。

  •  Consumer 与 partition

       其实上面所说的订阅关系还不够明确,其实topic中的partition被分配到某个consumer上,也就是某个consumer订阅了某个partition。 再重复一下:consumer订阅的是partition,而不是message。所以在同一时间点上,订阅到同一个partition的consumer必然属于不同的Consumer Group。

       在官方网站上,给出了这样一张图:


       一个kafka cluster中的某个topic,有4个partition。有两个consumer group (A and B)订阅了该topic。 Consumer Group A有2个partition:p0、p1,Consumer Group B有4个partition:c3,c4,c5,c6。经过分区分配后,consumer与partition的订阅关系如下。

Topic 中的4个partition在Consumer Group A中的分配情况如下:

C1 订阅p0,p3

C2 订阅p1,p2

Topic 中的4个partition在Consumer Group B中的分配情况如下:

C3 订阅p0

C4 订阅p3

C5 订阅p1

C6 订阅p2 

另外要知道的是,partition分配的工作其实是在consumer leader中完成的。

  • Consumer 与Consumer Group

       Consumer Group与Consumer的关系是动态维护的:

当一个Consumer 进程挂掉 或者是卡住时,该consumer所订阅的partition会被重新分配到该group内的其它的consumer上。当一个consumer加入到一个consumer group中时,同样会从其它的consumer中分配出一个或者多个partition 到这个新加入的consumer。

       当启动一个Consumer时,会指定它要加入的group,使用的是配置项group.id。为了维持Consumer 与 Consumer Group的关系,需要Consumer周期性的发送heartbeat到coordinator(协调者,在早期版本,以zookeeper作为协调者。后期版本则以某个broker作为协调者)。当Consumer由于某种原因不能发Heartbeat到coordinator时,并且时间超过session.timeout.ms时,就会认为该consumer已退出,它所订阅的partition会分配到同一group 内的其它的consumer上。而这个过程,被称为rebalance。


<think>好的,用户想了解Flink读写Kafka的工作原理和实现机制。我需要结合之前的引用内容来构建回答。首先,Flink的Kafka连接器分为DataStream API和Table API两种方式,可能需要分别说明。 用户提到了原理和实现机制,所以需要深入解释Source和Sink的工作流程。比如,Kafka Consumer如何集成到Flink的Source中,如何分配分区,以及如何保证不同语义,比如exactly-once。引用[1]中提到了三种语义,特别是使用Kafka事务实现exactly-once,这部分需要详细说明,包括事务的提交和检查点机制。 在读取部分,可能需要提到消费者如何订阅主题,分区分配策略,偏移量管理,以及如何与Flink的检查点结合实现容错。引用[3]中的依赖和示例代码显示需要配置bootstrap.servers和groupId,这些配置可能影响消费者的行为。 写入方面,Kafka Producer的集成,如何批量发送,事务支持。引用[5]中的示例代码显示使用Table API创建表时指定了connector为kafka,format为json,这可能涉及到序列化方式和写入格式。同时,引用[2]提到可以写入事件时间戳到Kafka,这需要调用setWriteTimestampToKafka方法,说明时间戳的处理机制。 需要区分DataStream和Table API的不同实现方式,比如DataStream使用FlinkKafkaConsumer和FlinkKafkaProducer类,而Table API通过DDL定义表结构。引用[3]和[4]分别对应这两种方式,可能需要对比说明。 另外,容错机制部分,需要结合检查点机制,解释如何通过两阶段提交协议实现exactly-once。引用[1]中的配置项isolation.level需要与事务配合使用,确保消费者能读取已提交的数据。 最后,确保回答结构清晰,分读取和写入两部分,涵盖原理、分区管理、语义保证、API差异和容错机制。可能还需要生成相关问题,比如语义实现、性能优化、API选择等。</think>### Flink 读写 Kafka 的工作原理及实现机制 #### 1. **读取 KafkaKafka Source)** Flink 通过 **`FlinkKafkaConsumer`** 实现从 Kafka 的读取,其核心原理如下: - **分区分配与并行度** Flink 的 Kafka Consumer 会将 Kafka Topic 的分区均匀分配给所有并行任务。例如,若 Topic 有 4 个分区且 Flink Source 并行度为 2,则每个任务处理 2 个分区[^3]。 - **偏移量管理** Flink 在检查点(Checkpoint)机制中保存 Kafka 消费的偏移量。当任务失败时,Flink 会从最近一次检查点恢复偏移量,保证数据一致性[^1]。 - **语义保障** - **至少一次(At-Least-Once)**:默认模式,数据可能重复但不会丢失。 - **精确一次(Exactly-Once)**:需启用检查点,结合 Kafka 事务机制,确保消费和处理的原子性[^1]。 #### 2. **写入 KafkaKafka Sink)** Flink 通过 **`FlinkKafkaProducer`** 实现数据写入 Kafka,关键机制包括: - **事务性写入** 为实现 Exactly-Once 语义,Flink 使用 Kafka 事务。每个检查点周期内,Flink 开启一个事务批量写入数据,并在检查点完成时提交事务[^1]。 示例配置: ```java producer.setTransactionalId("txn-id-prefix"); ``` - **时间戳写入** 可通过 `setWriteTimestampToKafka(true)` 将 Flink 记录的 Event-Time 时间戳写入 Kafka 消息[^2]。 - **格式序列化** 支持 JSON、Avro 等格式(需在 DDL 中指定 `format` 参数),例如 Table API 中定义: ```sql CREATE TABLE KafkaTable (...) WITH ('format' = 'json'); [^4][^5] ``` #### 3. **容错与一致性** - **检查点机制** Flink 的检查点会同步 Kafka 的偏移量和事务状态,确保 Source 和 Sink 的协同恢复[^1]。 - **两阶段提交(2PC)** 在 Exactly-Once 模式下,Flink 协调 Kafka Producer 的事务提交与检查点完成,避免数据重复或丢失[^1]。 #### 4. **DataStream API 与 Table API 对比** | **特性** | **DataStream API** | **Table API** | |------------------|---------------------------------------|----------------------------------------| | **实现方式** | 直接操作 `FlinkKafkaConsumer/Producer` | 通过 DDL 定义表结构 | | **灵活性** | 高(支持自定义逻辑) | 中(依赖 SQL 或 Table 函数) | | **语义配置** | 需显式设置事务 ID 和语义级别 | 通过 WITH 参数配置(如 `connector`) | | **适用场景** | 复杂流处理逻辑 | 结构化数据处理或 SQL 分析 | [^3][^4][^5] #### 5. **关键配置示例** - **消费者配置** ```properties bootstrap.servers = kafka:9092 group.id = flink-consumer-group isolation.level = read_committed # 配合事务使用 ``` - **生产者配置** ```properties transaction.timeout.ms = 600000 # 需大于检查点间隔 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值