Kafka 复习知识点

消息队列的主要应用场景:

1. 缓冲 / 消峰:双十一秒杀活动,用户量暴增。

2. 解耦:不同应用之间的解耦。数据源不可用不会影响下游应用程序

3. 异步通信:用户注册后,系统需要发送注册短信给用户。

消息队列的两种模式:

1. 点对点模式

2. 发布/订阅模式

1 集群架构

名词说明
Producer消息生产者
Consumer消息消费者
Consumer Group多个消费者组成的消费者组
BrokerKafka 服务端节点,一个 Kafka 集群由多个 Broker 组成
Topic消息主题
Partition一个主题 Topic 可以分为多个分区 Partition
Replica副本。一个主题的每个分区都有若干个副本,包含一个 Leader 和若干个 Follower
Leader能被生产者和消费者读写的副本
Follower只做备份的副本,保持和 Leader 数据同步。Leader 发生故障时,某个 Follower 会成为新的 Leader(Follower 一定是与 Leader 在不同的主机上)

2 生产者

2.1 消息发送流程

传输的消息数据包含主题 topic 、键 key、值 value、分区 partition、时间戳 timestamp 五个部分。

在消息发送的过程中,涉及到 main 线程和 Sender 发送线程。main 线程将消息发送给数据收集器 RecordAccumulator(底层是一个双端队列),Sender 线程则不断从该双端队列中拉取消息发送到 Kafka Broker。

2.2 发送方式

2.2.1 带回调函数的异步发送(推荐)

生产者消息发送到数据收集器,和数据收集器中的消息发送到 Kafka 集群,这两个发送阶段是异步的。

回调函数有两个参数,分别是元数据信息和异常信息,如果异常信息为 null ,说明消息发送成功,如果异常信息不为 null ,说明消息发送失败。

创建 maven 工程,导入依赖:

<dependencies>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>3.0.0</version>
    </dependency>
</dependencies>
public class CustomProducerCallback {
    public static void main(String[] args) throws InterruptedException {
        // 创建 kafka 生产者的配置对象
        Properties properties = new Properties(); 
        // 连接 kafka 
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop101:9092", "hadoop102:9092");
        // key, value 序列化
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        
        // 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);

        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first", 0, "", "s" + i), new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        System.out.println("主题:" + metadata.topic() + "-->" + "分区:" + metadata.partition);
                    } else {
                        exception.printStackTrace();
                    } 
                }
            });
             
            Thread.sleep(2);
        }
         kafkaProducer.close();
    }
}

2.2.2 同步发送

数据收集器中的数据正常发送到 Kafka 集群后,外部数据再发送到数据收集器中:

public class CustomProducerCallback {
    public static void main(String[] args) throws InterruptedException {
        Properties properties = new Properties(); 
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop101:9092", "hadoop102:9092");
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        
        // 只需要在异步发送的基础上,再调用 get() 方法即可
        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first", "s" + i)).get();
        }
        
        kafkaProducer.close();
    }
}

2.3 生产者分区

生产者使用分区器对发送的数据进行分区,实现负载均衡的同时,也可以提高发送消息的并行度。消费者可以以分区为单位消费数据。生产者默认分区策略如下:

  1. 指明分区的情况下,数据都写入该分区

  2. 未指明分区的情况下

    1. 如果有指明 key 值,根据 key 的哈希值与 topic 的分区数进行取余得到分区值
    2. 如果没有指明 key 值,则采用黏性分区器,随机选择一个分区并尽可能使用,直到该分区 批处理已满或发送完成(再次随机选择一个分区,和上一次的分区不同)

2.4 生产者参数

2.4.1 序列化

参数名称参数类型描述
key.serializerclass指定 key 的序列化器类名
value.serializerclass指定 value 的序列化器类名

2.4.2 连接参数

参数名称参数类型描述
bootstrap.serversnon-null string指定 Kafka 集群的初始连接的主机 / 端口列表,格式为host1:port1,host2:port2,…
client.idstring指定生产者对应的客户端 id,用来标记消息是从哪个客户端发来的)
connections.max.idle.mslong指定多久之后关闭闲置的连接,单位 ms,默认 9 分钟
max.request.sizeint指定生产者能发送消息的最大值,默认 1m

2.4.3 吞吐量

参数名称参数类型描述
buffer.memorylong指定数据收集器内部的缓冲区总大小,默认 32m
batch.sizeint指定每批次数据的最大总量,默认 16k。如果设置为 16k,数据收集器每接收到 16k 的数据就将其分组为一个批次进行发送
linger.mslong指定每批次数据的最大等待时间,单位 ms,默认为 0,生产环境建议该值大小为 5-100ms 之间。数据收集器在 linger.ms 时间段内接收的数据分组为一个批次进行发送
compression.typestring指定生产者发送数据的压缩方式,有效值为 [none, gzip, snappy, lz4, zstd] 之一

2.4.4 分区

参数名称参数类型描述
partitioner.classclass指定生产者分区器。如果没有设置,则使用默认的分区器 RangePartitioner;如果设置为 RoundRobinPartitioner,使用 RoundRobinPartitioner;如果设置为自定义分区器,只要让自定义分区器实现 org.apache.kafka.clients.producer.Partitioner 接口

2.4.5 数据可靠性

如果要保证数据至少发送一次,acks 需要设置为 -1,且分区副本和 ISR 队列里的副本数量至少为 2。

参数名称参数类型描述
acksstring0:生产者发送过来的数据,不需要等待服务器的任何确认1:生产者发送过来的数据,Leader 收到数据后应答-1(all):生产者发送过来的数据,Leader 和 ISR 队列里面的所有节点收齐数据后应答默认值是 -1(all)

2.4.6 数据不重复

幂等性保证生产者无论发送多少次数据,Broker 只会持久化一条,保证数据不重复,但是 Kafka 的幂等性只能保证一次会话的单个分区内数据不重复。

Kafka 开启幂等性需要以下几个参数配合使用:

参数名称参数类型描述
enable.idempotenceboolean指定是否开启幂等性,默认 true
retriesint指定生产者重试次数,如果启用幂等性,重试次数需要大于 0
retry.backoff.mslong指定两次重试之间的时间间隔,单位 ms,默认 100ms

如果要保证 Kafka 故障重启后也不会发送重复的数据,需要在代码中开启 Kafka 事务。

参数名称参数类型描述
transactional.idstring指定事务 id,事务 id 必须要唯一。如果没有提供事务 id,则不能使用事务。如果配置了事务 id,则隐含幂等性配置 enable.idempotence

2.4.7 数据不乱序

参数名称参数类型描述
enable.idempotenceboolean指定是否开启幂等性,默认 true
max.in.flight.requests.per.connectionint指定 Kafka 服务端缓存生产者数据的最大数量,默认为 5。如果 enable.idempotence 为 false,且想要保证数据不乱序,该值必须设为 1;如果 enable.idempotence 为 true,这个值必须小于等于 5。假如设置为 5,表明 Kafka 服务端缓存生产者发来的最近 5 个请求的数据,且这 5 个请求的数据一定是有序的

3 Broker

3.1 Broker 工作流程

  1. 每个 Broker 节点启动后,Broker 上的 Controller 会在 Zookeeper 中注册节点信息
  2. 最先注册的 Controller 成为 Controller Leader,监听其它 Broker 节点变化
  3. Controller Leader 来进行副本的 Leader 选举。在 ISR 中存活,并且在 AR 中排在前面的副本成为 Leader
  4. 该 Broker 节点的 Controller 将节点信息上传给 Zookeeper,其它 Broker 节点的 Controller 同步该信息

3.2 副本 ISR 机制

Kafka 副本相关的三个概念:

  • AR(Assigned Repllicas):一个分区的所有副本(AR = ISR + OSR)
  • ISR(In-Sync Replicas):能够和 Leader 保持同步的 Follower + Leader 本身组成的集合
  • OSR(Out-Sync Relipcas):不能和 Leader 保持同步的 Follower 集合

ISR 机制中的三个概念:

  • LEO(Log End Offset,每个 Follower 最后一个 offset,LEO = 最新的 offset +1)
  • HW(High Watermark,高水位线,所有 Follower 中最小的 LEO)
  • LSO(Log Start Offset,每个 Follower 的第一个 offset),普通消费者可见的消息范围就是 [LSO, HW)

3.2.1 Follower 故障

Follower 故障后会被临时踢出 ISR,这期间内它副本继续接收数据。等到该 Follower 恢复后,读取本地磁盘记录的上次的 HW,将日志文件高于 HW 的部分截掉(该部分数据没有经过校验,不能保证副本数据一致性,所以将其截掉重新同步 ),从 HW 开始向 Leader 同步。直到 Follower 的 LEO 大于等于该分区的 HW,就可以重新加入 ISR。

3.2.2 Leader 故障

Leader 故障后,从 ISR 中重新选出一个新的 Leader。为了保证副本之间的数据一致性,其余 Follower 将各自的日志文件高于 HW 的部分截掉,然后从新的 Leader 中同步数据。

3.2.3 ISR 策略优缺点

保证了数据一致性,但不能保证数据不丢失或不重复。

3.3 分区副本负载均衡

正常情况下,Leader 副本均匀分布在各个 Broker 的上,保证每台机器极可能读写负载均衡。但是如果某些 Broker 节点宕机,会导致 Leader 副本过于集中在存活的 Broker 上,造成集群负载不均衡。

3.3.1 分区副本负载均衡参数

参数名称参数类型描述
auto.leader.rebalance.enableboolean指定是否开启 Leader 副本的负载均衡, 默认 true。生产环境中建议设为 false
leader.imbalance.per.broker.percentageint如果开启了 Leader 副本的负载均衡,指定每个 Broker 允许的不平衡的 Leader 副本的比例,默认 10%。如果每个 Broker 超过了这个值,会触发 Leader 副本的分区负载均衡
leader.imbalance.check.interval.secondslong指定检查 Leader 副本是否负载均衡的间隔时间,默认 300s

3.4 文件存储

Topic 主题是一个逻辑上的概念,而主题的 partition 分区是一个物理上的概念,即每个 partition 都有一个对应的 log 文件,存储生产者生产的数据。生产者生产的数据都是顺序写在日志文件末尾。

为了防止 log 文件过大导致数据查找效率低,Kafka 采取了分片和索引机制,将每个 partition 分为多个 segment,每个 segment 包括 index 文件、log 文件和 timeindex 等文件,这些文件位于同一个文件夹下,该文件夹命名为【主题名-分区序号】。

index 文件和 log 文件以当前 segemnt 的第一条消息的 offset 命名。每往 log 文件中写入 4kb 数据,就往 index 文件中写入一条索引。

3.4.1 日志参数

参数名称参数类型描述
log.segment.bytesint指定 log 日志分成的 segmernt 大小,默认 1g
log.index.interval.bytesint指定写入稀疏索引的数据量间隔,默认 4kb。即每往 log 文件写入 4kb 数据,会往 index 文件写入一条索引
log.retention.hoursint指定日志保存小时数,默认保存 7 天
log.cleanup.policylist指定日志清理策略,有效值为 compact 和 delete。delete:所有数据启用删除策略;compact:所有数据启用压缩策略。对于相同 key 的不同 value 值,只保留最后一个版本。这种策略只适合特殊场景,比如消息的 key 是用户 id,value 是用户的资料,通过这种压缩策略,整个消息集里就保存了所有用户最新的资料

3.5 Broker 参数

参数名称参数类型描述
broker.idint指定 Broker 节点的唯一 id
log.dirsstring指定存储日志数据的目录列表,目录列表以逗号分隔
zookeeper.connectstring指定 ZooKeeper 连接字符串,格式为 hostname1:port1,hostname2:port2,hostname3:port3
replica.lag.time.max.mslong指定 ISR 队列中 Follower 向 Leader 发送通信请求的时间阈值,默认 30s。超过该时间未通信的 Follower 将被踢出 ISR
replica.fetch.wait.max.msint指定 Follower 副本向 Leader 发送通信请求的最大等待时间,默认 500ms。这个值应该始终小于 replica.lag.time.max.ms,以防止低吞吐量的主题频繁收缩 ISR 队列

4 消费者

4.1 消费者总体工作流程

  1. 消费者组内每个消费者消费不同分区的数据,一个分区只能由一个组内消费者消费。消费者组之间互不影响
  2. 每个消费者的 offset 由消费者提交到其消费的分区中进行保存

4.2 消费者组初始化流程

每个 Broker 服务器都有一个 coordinator 来辅助消费者组的分区分配和初始化。根据消费者组的 groupid 哈希值 % 50 (假设 _comsumer_offsets 的分区数为 50)来选择哪个 Broker 服务器的 coordinator 作为消费者组的老大。该消费者组下所有的消费者提交 offset 时也提交到对应的 _comsumer_offsets 分区。

4.3 消费者组消费流程

4.4 消费者相关参数

参数名称参数类型描述
group.idstring指定消费者所属的消费者组 id
session.timeout.msint指定消费者和 coordinator 之间连接超时时间,默认 45s。如果连接时间超过该值,该消费者被移除,消费者组执行再平衡
max.poll.interval.msint消费者处理消息的最大时长,默认 5 分钟。如果处理时间超过该值,该消费者被移除,消费者组执行再平衡
fetch.min.bytesint消费者获取服务器端一批消息最小的字节数,默认 1 byte
fetch.max.bytesint消费者获取服务器端一批消息最大的字节数,默认 55 m
fetch.max.wait.msint如果没有从服务器端获取到一批数据的最小字节数。该时间到,仍然会返回数据,默认 500ms
max.poll.recordsint消费者一次拉取数据返回消息的最大条数,默认是 500

4.5 消费者分区策略参数

一个消费者组中有多个消费者组成,一个主题有多个分区组成,到底由哪个消费者来消费哪个分区的数据,就是 Kafka 的分区分配策略。

参数名称参数类型描述
partition.assignment.strategylist指定消费者分区策略

可以使用的策略有:

  • org.apache.kafka.clients.consumer.RangeAssignor,基于主题分区,见 4.4.1

  • org.apache.kafka.clients.consumer.RoundRobinAssignor,轮询分区,见 4.4.2

  • org.apache.kafka.clients.consumer.CooperativeStickyAssignor,遵循相同的 StickyAssignor 逻辑,但允许 cooperative 进行再平衡

    默认策略是 Range + CooperativeSticky。

4.5.1 RangeAssignor

4.5.2 RoundRobinAssignor

4.6 消费者 offset

4.6.1 offset 的默认维护位置

消费者默认将 offset 保存在 kafka 的内置系统主题 _consumer_offsets 中。_consumer_offsets 主题里面采用 key 和 value 的方式存储数据。key 是 group.id + topic + 分区号,value 就是当前 offset 的值。每隔一段时间,kafka 内部会对这个系统主题 _consumer_offsets 进行 compact,保留最新数据。

4.6.2 offset 参数

参数名称参数类型描述
offsets.topic.num.partitionsint__consumer_offsets 的分区数,默认为 50。部署后不建议修改
enable.auto.commitboolean指定是否开启自动提交 offset 功能,默认是 true
auto.commit.interval.msint指定自动提交 offset 的时间间隔,默认是 5s
auto.offset.resetstring指定 offset 消费方式:1. earliest:offset 重置为最早的偏移量
  1. latest(默认):offset 重置为最新的偏移量
  2. none:未找到消费者组之前的偏移量,则向消费者抛出异常 |

5 Kafka-Kraft 模式

Kafka-Kraft 模式架构不再依赖 Zookeeper 集群,而是用三台 controller 节点代替 Zookeeper,元数据保存在 controller 中,由 controller 直接进行 Kafka 集群管理。这样做的好处有以下几个:

  1. Kafka 不再依赖外部框架,而是能够独立运行
  2. controller 管理集群时,不再需要从 Zookeeper 中先读取数据,集群性能上升
  3. 由于不依赖 Zookeeper,集群扩展时不再受到 Zookeeper 读写能力限制
  4. controller 不再动态选举,而是由配置文件规定。这样我们可以有针对性的加强 controller 节点的配置,而不是像以前一样对随机 controller 节点的高负载束手无策

5.1 Kraft 集群部署

解压一份 kafka 安装包:

tar -zxvf kafka_2.12-3.0.0.tgz -C /opt/module/
mv kafka_2.12-3.0.0/ kafka

101 上修改 /opt/module/kafka/config/kraft/server.properties 配置文件:

# kafka 的角色(controller 相当于主机、broker 节点相当于从机,主机类似 zk 功能)
process.roles=broker, controller
# 节点 ID
node.id=1
# controller 服务协议别名 
controller.listener.names=CONTROLLER 
# 全 Controller 列表 
controller.quorum.voters=1@hadoop101:9093,2@hadoop102:9093,3@hadoop103:9093 
# 不同服务器绑定的端口 
listeners=PLAINTEXT://:9092,CONTROLLER://:9093 
# broker 服务协议别名 
inter.broker.listener.name=PLAINTEXT 
# broker 对外暴露的地址 
advertised.Listeners=PLAINTEXT://hadoop101:9092 
# 协议别名到安全协议的映射 
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL 
# kafka 数据存储目录 
log.dirs=/opt/module/kafka/data

102 和 103 中的 node.id 和 advertised.Listeners 也要作相应的修改。

先生成存储目录唯一 ID:

 bin/kafka-storage.sh random-uuid

拿着生成的 UUID 在三个节点上格式化 kafka 存储目录:

bin/kafka-storage.sh format -t  J7s9e8PPTKOO47PxzI39VA -c /opt/module/kafka/config/kraft/server.properties 

在三个节点的 kafka 目录下启动 kafka 集群:

bin/kafka-server-start.sh -daemon config/kraft/server.properties
  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 作为一个JavaAI助手,我认为Java面试复习应该从以下几个方面开始: 1. Java基础知识:包括Java语言特性、数据类型、流程控制语句、面向对象编程、异常处理、集合框架等。 2. Java虚拟机:包括内存模型、垃圾回收机制、类加载机制等。 3. 多线程:包括线程的创建、同步和互斥、线程池等。 4. Java Web开发:包括Servlet、JSP、Spring、MyBatis、Hibernate等。 5. 数据库:包括SQL语句、数据库设计、MySQL、Oracle等。 6. 网络编程:包括TCP/IP协议、HTTP协议、Socket编程等。 7. 设计模式:包括单例模式、工厂模式、观察者模式等。 8. 框架技术:包括Spring Boot、Spring Cloud、Dubbo、Zookeeper、Kafka等。 以上是我认为Java面试复习的核心点,但具体复习的内容还要根据自己的实际情况和职位要求来定。 ### 回答2: 在准备Java面试时,首先应该从基础知识开始复习。以下是一些建议的复习内容: 1. Java基础知识:复习Java的数据类型、控制结构、面向对象的概念和特性、异常处理、集合类等基础知识。还应该熟悉Java中常用的关键字和操作符。 2. 面向对象编程:复习关于封装、继承和多态的概念以及如何在Java中实现它们。还应该了解设计模式和SOLID原则等面向对象编程的相关概念。 3. Java核心类库:复习Java的核心类库,包括IO流、多线程、网络编程以及常用工具类等。特别要注意Java 8引入的新特性,如Lambda表达式和Stream API。 4. 数据库知识:熟悉SQL语言和关系型数据库的基本概念,了解JDBC的使用和常见的数据库操作。 5. 数据结构和算法:复习常用的数据结构,如数组、链表、栈、队列、树和图等。还应该了解常见的排序和搜索算法,并能够分析它们的时间复杂度。 6. 框架和技术:了解常用的Java框架和技术,如Spring、Hibernate、Java Servlet、JSP和MVC等。还应该了解Web开发中的常见技术,如HTML、CSS、JavaScript和HTTP协议。 7. 掌握项目经验:复习自己的项目经验,了解自己在项目中所承担的角色和责任,并准备有关项目的技术细节和相关问题的回答。 在复习过程中,建议通过做一些模拟面试题和编程练习来巩固所学的知识。同时也要保持对新技术和新特性的学习,如Java 11、Java模块系统和响应式编程等。最重要的是保持对Java编程的实践和理解,通过项目实践和编码实践来提升自己的能力。 ### 回答3: Java面试的复习应该从以下几个方面开始: 1. 基本语法和核心概念:复习Java的基本语法规则、关键字、变量数据类型、运算符以及流程控制语句等。同时还需要熟悉面向对象的概念,如类和对象、继承、多态等。 2. 集合框架:复习Java集合框架的常见类,如ArrayList、LinkedList、HashSet、HashMap等,了解它们的特点、常用方法以及适用场景。同时还需了解迭代器和遍历方式等相关知识。 3. 异常处理:复习Java的异常处理机制,包括异常类的继承关系、异常处理的方式和操作等。了解如何捕获和处理异常,并了解常见的异常类型及其解决方法。 4. 多线程:复习Java的多线程编程相关知识,包括线程的基本概念、创建和启动线程的方式、线程同步与互斥、线程池的使用等。同时了解Java中的线程状态和线程调度等内容。 5. IO流和网络编程:复习Java的IO流操作,包括文件的读写、字节流和字符流的区别、以及常见的输入输出流等。还需要了解Java网络编程的基本概念、Socket编程、URL和URLConnection等相关知识。 6. JDBC和数据库:复习Java数据库连接(JDBC)相关知识,包括数据库的基本操作、连接数据库的方式、执行SQL语句以及事务处理等。同时还需了解常见的数据库操作类和框架,如Hibernate和MyBatis等。 除了以上内容,还建议在复习过程中多动手实践,编写一些简单的Java程序或者参与一些项目实践,以提升对知识的理解和运用能力。另外,对于面试经常涉及的常见问题和算法题也应进行重点复习,例如字符串处理、排序算法、数据结构等。最后,了解一些面试技巧和注意事项,如自我介绍、项目经验的准备和回答等,有助于更好地应对面试的挑战。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值