关于Kafka,你需要知道的都在这

本文深入介绍了Kafka消息系统,包括其异步通信、发布订阅模式以及Kafka架构中的ISR机制。讨论了一致性原则,如消费者和存储一致性,并解析了消费者分配策略。同时,讲解了Kafka的安装配置步骤和常用操作,以及Windows和Linux环境下的Producer和ConsumerAPI使用。此外,还提出了常见的问题及解决方案,如解决消费堵塞和实现幂等性。

1.消息系统

消息队列
  • 异步通信
  • 实现削峰操作
  • 解决生产者,消费者处理消息不一致
发布订阅模式
  • 消费者主动拉取消息(kafka) 缺点:若队列中没有消息,消费者任然会不断询问是否有消息
  • 队列发送给消费者(公众号) 缺点:消费者的处理能力不同会造成资源浪费或者处理过载
kafka架构

在这里插入图片描述

  • 消费者只会从leader拉取消息,follower只负责备份,同一个消费组里的消费者不能消费同一个分区的消息

  • zookeeper存储Kafka的集群信息

  • 在 Kafka 中的每一条消息都有一个Topic。生产者生产的每条消息只会被发送到一个Partition中,通过Offset 可以确定在该Partition下的唯一消息。Broker负责接收和处理客户端发送过来的请求,以及对消息进行持久化。

  • ISR机制:
    topic中每一个leader接收到消息后会传给其他副本,若某一个副本发生故障没有同步leader的消息,会被剔除ISR;若leader发生故障,会在ISR中的follower中推选出新的leader

一致性原则

在这里插入图片描述

  • 消费者一致性

    ​ 当某个leader发生故障后,ISR中的新leader与其他follower的存储是不一致的,标注所有副本中的LEO(log end offset)即最大offect,确定队列中的HW(high watermark)即ISR队列中最小的LEO。HW之前的消息才会对消费者可见。

  • 存储一致性

    ​ 故障发生后由于新leader与follower的存储是不一致的,系统会将follower中高于HW的部分全部裁掉,然后从新leader同步数据。

消费者分配策略

该策略只有在消费者数量发生变化时才生效

  • RoundRobin(轮询):轮询消费partition,但必须保证消费组中所有消费者订阅的是同一个topic

  • Range(默认):

    举个栗子:

    有两个主题t1,t2,它们都有三个分区;有两个消费者组,第一组里有A,B两个消费者,第二组里有消费者C;其中A订阅了t1,B订阅了t1,t2,C订阅了t1
    在这里插入图片描述

文件存储结构

在这里插入图片描述
每一个topic中存储着不同的partition,生产者会不断地将消息追加到log文件中,为了防止文件过大,log又会分为多个segment,每个segment都有两个文件,其中log中存储消息数据,index存储大小相同的偏移量

常见问题
  1. kafka堵塞的原因/解决

    ​ 消费能力不足:增加topic的分区数量,增加消费者,二者要相等
    ​ 下游数据处理不及时:提高每次拉取的数量

  2. 如何实现幂等性

    ​ Producer 的幂等性指的是当发送同一条消息时,数据在 Server 端只会被持久化一次,数据不丟不重 ,但是仅限于单个partition中,跨partition 需要使用 Kafka 的事务性来实现

2.安装配置

配置三台虚拟机,分别配置好zookeeper,Java

将kafka的安装包分别解压到/usr/local,在config目录中修改如下内容

#Slave1中
broker.id=0
zookeeper.connect=Slave1:2181,Slave2:2181,Slave3:2181
log.dirs=/usr/local/kafka/log
#Slave2中
broker.id=1
zookeeper.connect=Slave1:2181,Slave2:2181,Slave3:2181
log.dirs=/usr/local/kafka/log
#Slave3中
broker.id=2
zookeeper.connect=Slave1:2181,Slave2:2181,Slave3:2181
log.dirs=/usr/local/kafka/log

在三台虚拟机上手动启动zookeeper,cd到kafka目录下,输入命令在后台运行

#启动服务
zkServer.sh start
bin/kafka-server-start.sh -daemon config/server.properties
Kafka常用操作
#创建名为test02_02的topics (2 partitions & 2 replication-factor)
bin/kafka-topics.sh --bootstrap-server Slave1:9092 --create --topic test_02_02 --replication-factor 2 --partitions 2
#查看topics
bin/kafka-topics.sh --list --zookeeper Slave1:2181
#查看topics详细信息
bin/kafka-topics.sh --bootstrap-server Slave1:9092 --describe --topic test_02_02

#Topic: test_02_02	Partition: 0	Leader: 1	Replicas: 1,2	Isr: 1,2
#Topic: test_02_02	Partition: 1	Leader: 0	Replicas: 0,1	Isr: 0,1

#创建生产者(发送消息)
bin/kafka-console-producer.sh --broker-list Slave1:9092,Slave2:9092,Slave3:9092 --topic test_02_02
#创建消费者(接收消息)
#–from-beginning:会把topic中以往所有的数据都读取出来,只读取partition 0
bin/kafka-console-consumer.sh --bootstrap-server Slave1:9092 --topic test_02_02 --partition 0 --from-beginning

3.开发环境API

1.ProducerAPI:在windows端发送消息,指定partition接收
public class TestProducer {
    public static void main(String[] args){
        String topic = "test_02_02";
        Map<String,Object> kafkaProperties = new HashMap<>();

        kafkaProperties.put("bootstrap.servers","Slave1:9092,Slave2:9092,Slave3:9092");
        kafkaProperties.put("acks", "all");

        kafkaProperties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        kafkaProperties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        Producer<String, String> producer = new KafkaProducer<>(kafkaProperties);
//指定partition 0 接收
        for (int i = 0; i < 10; i++)
            producer.send(new ProducerRecord<>(topic, 0,"key_"+ Integer.toString(i), Integer.toString(i)));

        producer.close();
        System.out.println("消息发送完成!");
    }
}
2.ConsumerAPI:在Linux端发送数据到指定partition,windows端接收
public class TestConsumer {
    public static void main(String[] args){
        String topic = "test_02_02";
        String group = "test_group";
        Map<String,Object> kafkaProperties = new HashMap<>();
	 kafkaProperties.put("bootstrap.servers","Slave1:9092,Slave2:9092,Slave3:9092");
        kafkaProperties.put("group.id", group);
        kafkaProperties.put("enable.auto.commit","true");


        kafkaProperties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        kafkaProperties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        KafkaConsumer<String,String> consumer = new KafkaConsumer<>(kafkaProperties);
        consumer.assign(Arrays.asList(new TopicPartition(topic,0)));
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
            for (ConsumerRecord<String, String> record : records)
                System.out.printf("offset = %d, key = %s, value = %s%n ", record.offset(), record.key(), record.value());
        }
    }
}
<think>嗯,用户之前已经问过关于Kafka的ISR和ID的问题,现在又接着问ZooKeeper的工作流程。这说明用户可能是在学习Kafka的整体架构,想要了解各个组件之间的关系。我需要先确定用户对Kafka的基本概念是否熟悉,但根据之前的对话,他们已经了解了一些核心概念,所以可以深入一点。 用户提到的是ZooKeeper在Kafka中的工作流程。首先,我得回忆一下Kafka和ZooKeeper的关系。Kafka在旧版本中重度依赖ZooKeeper,用于元数据管理、控制器选举、Broker注册等。不过在较新版本中,Kafka开始逐步脱离ZooKeeper,比如KIP-500引入的KRaft模式,但用户可能还在使用依赖ZooKeeper的版本,所以需要详细解释传统架构下的流程。 接下来需要梳理ZooKeeper在Kafka中的具体职责。比如Broker注册、Topic配置管理、分区和副本状态、控制器选举、消费者群组协调等。要分步骤说明每个流程是如何工作的,比如Broker启动时如何注册自己,控制器如何选举,分区Leader如何分配,消费者如何利用ZooKeeper协调。 同时要注意用户可能对某些术语不太熟悉,比如控制器(Controller)、ISR(之前已经提过),需要适当关联之前的内容。比如在Broker注册时,ZooKeeper存储Broker的ID和地址,这样控制器可以知道集群中的Broker列表,进而进行分区分配。 还需要考虑用户可能的疑问点,比如为什么需要控制器选举,ZooKeeper如何监听节点变化,当Broker宕机时如何触发故障转移。这些流程需要以步骤的形式呈现,比如Broker注册、控制器选举、元数据同步、故障处理等阶段。 另外,用户可能对ZooKeeper的高可用性和一致性机制感兴趣,比如ZAB协议,但问题主要集中在工作流程,所以可能需要简要提及ZooKeeper如何保证数据一致性,但不过多深入内部机制。 最后,考虑到Kafka正在向去除ZooKeeper的方向发展,可能需要补充说明新版本的变化,让用户了解趋势,但重点还是在传统工作流程上。同时确保回答结构清晰,分点说明,结合示例或参数配置,比如ZooKeeper的路径结构,帮助用户理解存储的数据结构。</think>以下是Kafka依赖ZooKeeper时的核心工作流程详解(基于传统架构,适用于Kafka 2.8之前的版本): --- ### 一、核心角色分工 | 组件 | 职责 | |-------------|------------------------------| | **ZooKeeper** | 分布式协调服务,存储元数据和集群状态 | | **Kafka Broker** | 数据存储与消息处理节点 | | **Controller** | 特殊Broker,通过ZooKeeper选举产生 | --- ### 二、核心工作流程 #### 1. **Broker注册流程** - **注册路径**:`/brokers/ids/[broker.id]` - **步骤**: 1. Broker启动时向ZooKeeper的`/brokers/ids`注册临时节点(Ephemeral Node) 2. 节点内容包含:`{ "host": "192.168.1.10", "port": 9092, "version": 4 }` 3. 若Broker宕机,临时节点自动消失触发集群状态更新 #### 2. **Controller选举** - **选举机制**: 1. 所有Broker竞争创建`/controller`临时节点 2. 第一个成功创建的Broker成为Controller 3. 若Controller宕机,节点消失触发重新选举 - **Controller职责**: - 分区Leader选举 - 副本分配管理 - 集群扩缩容处理 #### 3. **Topic元数据管理** - **存储路径**: - 配置信息:`/config/topics/[topic-name]` - 分区分配:`/brokers/topics/[topic-name]` - **创建流程**: 1. 用户通过`kafka-topics.sh`创建Topic 2. ZooKeeper在`/brokers/topics`下记录分区副本分配方案 3. Controller监听该节点变化,同步元数据到所有Broker #### 4. **消费者群组协调** - **旧版消费者(已淘汰)**: - 偏移量存储路径:`/consumers/[group-id]/offsets/[topic]/[partition]` - 消费者注册路径:`/consumers/[group-id]/ids/[consumer-id]` - **新版消费者(使用__consumer_offsets Topic)**: ZooKeeper仅保留兼容支持,主要数据迁移至Kafka内部Topic #### 5. **分区状态同步** - **ISR变更**: 1. Leader将ISR变化写入`/brokers/topics/[topic]/partitions/[p]/state` 2. Controller监听该节点,触发元数据广播 - **Leader选举**: 1. Controller检测到Leader下线 2. 从ISR中选择新Leader,更新ZooKeeper中的分区状态 --- ### 三、关键ZooKeeper路径结构 ```shell / ├── brokers │ ├── ids # 在线Broker列表(临时节点) │ ├── topics # Topic分区分配方案 │ └── seqid # 全局唯一ID生成 ├── config │ ├── topics # Topic级别配置覆盖 │ └── clients # 客户端级配置 ├── controller # 当前Controller信息(临时节点) ├── controller_epoch # Controller纪元版本(防脑裂) └── admin └── delete_topics # Topic删除标记 ``` --- ### 四、典型场景示例 #### 场景1:Broker故障恢复 1. Broker-3宕机,其`/brokers/ids/3`节点消失 2. Controller检测到节点变化 3. 重新计算受影响分区的ISR集合 4. 若原Leader在Broker-3上,触发分区Leader切换 #### 场景2:Topic扩容 1. 用户执行`kafka-topics --alter --partitions 4` 2. ZooKeeper更新`/brokers/topics/[topic]`数据 3. Controller监听到变更,计算新分区的副本分配 4. 将新分区元数据同步给所有Broker --- ### 五、参数配置关键点 ```properties # ZooKeeper连接配置 zookeeper.connect=zk1:2181,zk2:2181,zk3:2181/kafka # Session超时(默认18秒) zookeeper.session.timeout.ms=18000 # 连接超时(默认6秒) zookeeper.connection.timeout.ms=6000 ``` --- ### 六、Kafka去ZooKeeper化趋势 自Kafka 2.8版本起,通过**KRaft模式**(使用内部Raft协议)逐步替代ZooKeeper,核心变化: 1. 元数据存储在内部`__cluster_metadata` Topic 2. Controller角色分为Active/Standby模式 3. 完全移除ZooKeeper依赖(目标版本3.0+) --- **注**:生产环境若使用Kafka 3.0+版本,建议优先选择KRaft模式以获得更好的性能和运维简化。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值