Kafka笔记

Kafka笔记

概述

[外链图片转存中…(img-ntYqtl5H-1705932725612)]

消息队列

传统的消息队列的主要应用场景:缓冲/削峰 ,解耦,异步通信

  • 缓冲/削峰:有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况

    [外链图片转存中…(img-QoumkeKk-1705932725613)]

  • 解耦:允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束

    [外链图片转存中…(img-u6nS1mbi-1705932725613)]

  • 异步通信:允许用户把一个消息放入队列,但并不立即处理它,然后在需要的时候再去处理它们

    [外链图片转存中…(img-MPTYLk91-1705932725613)]

消息队列的两种模式

  1. 点对点模式——消费者主动拉取数据,消息收到后清除消息

    [外链图片转存中…(img-IwY24lWd-1705932725614)]

  2. 发布/订阅模式

    • 可以有多个topic主题
    • 消费者消费数据之后,不删除数据
    • 每个消费者相互独立,都可以消费到数据

[外链图片转存中…(img-B8SzNQrf-1705932725614)]

基础架构

生产和消费只针对leader,leader挂掉之后,follower有条件才能成为leader

首先100T的数据怎么存储,把数据切割为分区

[外链图片转存中…(img-ZXonY0MW-1705932725614)]

  1. Producer:消息生产者,就是向Kafka broker发消息的客户端
  2. Consumer:消息消费者,向Kafka broker取消息的客户端
  3. Consumer Group(CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
  4. Broker:一台Kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
  5. Topic:可以理解为一个队列,生产者和消费者面向的都是一个topic
  6. Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition都是一个有序的队列
  7. Replica:副本。一个topic的每个分区都有若干个副本,一个leader和若干个follower
  8. Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是Leader
  9. Follower:每个分区多个副本的"从",实时从Leader中同步数据,保持和Leader数据的同步。Leader发送故障时,某个Follower会成为新的Leader。

入门

安装

/tmp Linux临时目录 会被定时清理

在 zk 根目录下创建/kafka,方便管理 只用删除一个包

进入到/opt/module/kafka 目录,修改配置文件

cd config/
vim server.properties

输入以下内容

#broker 的全局唯一编号,不能重复,只能是数字。
broker.id=0
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘 IO 的线程数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka 运行日志(数据)存放的路径,路径不需要提前创建,kafka 自动帮你创建,可以
配置多个磁盘路径,路径与路径之间可以用","分隔
log.dirs=/opt/module/kafka/datas
#topic 在当前 broker 上的分区个数
num.partitions=1
#用来恢复和清理 data 下数据的线程数量
num.recovery.threads.per.data.dir=1
# 每个 topic 创建时的副本数,默认时 1 个副本
offsets.topic.replication.factor=1
#segment 文件保留的最长时间,超时将被删除
log.retention.hours=168
#每个 segment 文件的大小,默认最大 1G
log.segment.bytes=1073741824
# 检查过期数据的时间,默认 5 分钟检查一次是否数据过期
log.retention.check.interval.ms=300000
#配置连接 Zookeeper 集群地址(在 zk 根目录下创建/kafka,方便管理)
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181/ka
fka

分发完来修改broker.id,分别在 hadoop103 和 hadoop104 上修改配置文

件/opt/module/kafka/config/server.properties中的 broker.id=1、broker.id=2

xsync kafka/

注:broker.id 不得重复,整个集群中唯一

配置环境变量

vim /etc/profile.d/my_env.sh
#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka
export PATH=$PATH:$KAFKA_HOME/bin

刷新环境变量

source /etc/profile

分发环境变量文件到其他节点,并source

/xsync /etc/profile.d/my_env.sh

启动集群

  1. 先启动 Zookeeper 集群,然后启动 Kafka

    zk.sh start
    
  2. 依次在 hadoop102、hadoop103、hadoop104 节点上启动 Kafka

    bin/kafka-server-start.sh -daemon config/server.properties
    
  3. 关闭集群

    bin/kafka-server-stop.sh 
    

启动——停止脚本

脚本这一块要写绝对路径

chmod 754 filename

这句命令的意思是将filename文件的读写运行权限赋予文件所有者,把读和运行的权限赋予群组用户,把读的权限赋予其他用户。

对文件或目录设置777权限意味着它将对所有用户都是可读、可写和可执行的,并且可能会带来巨大的安全风险。

编写脚本

vim kf.sh
#! /bin/bash
case $1 in "start"){
 for i in hadoop102 hadoop103 hadoop104
 do
 echo " --------启动 $i Kafka-------"
 ssh $i "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
 done
};;
"stop"){
 for i in hadoop102 hadoop103 hadoop104
 do
 echo " --------停止 $i Kafka-------"
 ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh "
 done
};;
esac
  • 添加执行权限

     chmod +x kf.sh
    
  • 启动集群命令

     kf.sh start
    
  • 停止集群命令

    kf.sh stop
    

**注意:**停止 Kafka 集群时,一定要等 Kafka 所有节点进程全部停止后再停止 Zookeeper集群。因为 Zookeeper 集群当中记录着 Kafka 集群相关信息,Zookeeper 集群一旦先停止,Kafka 集群就没有办法再获取停止进程的信息,只能手动杀死 Kafka 进程了

topic 命令行操作

[外链图片转存中…(img-vvjtSoQC-1705932725615)]

查看操作主题命令参数

bin/kafka-topics.sh
参数描述
–bootstrap-server <String: server toconnect to>连接的 Kafka Broker 主机名称和端口号。
–topic <String: topic>操作的 topic 名称。
–create创建主题。
–delete删除主题。
–alter修改主题。
–list查看所有主题。
–describe查看主题详细描述
–partitions <Integer: # of partitions>设置分区数
–replication-factor<Integer: replication factor>设置分区副本
–config <String: name=value>更新系统默认的配置。
  1. 查看当前服务器中的所有topic

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --list
    
  2. 创建first topic

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --partitions 1 --replication-factor 3 --topic first
    

    选项说明:

    –topic 定义 topic 名

    –replication-factor 定义副本数

    –partitions 定义分区数

  3. 查看 first 主题的详情

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --describe --topic first
    
  4. 修改分区数(注意:分区数只能增加,不能减少)

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --alter --topic first --partitions 3
    
  5. 再次查看first 主题的详情

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --describe --topic first
    
  6. 删除 topic(学生自己演示)

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --delete --topic first
    

生产者命令行操作 端口9092

  1. 查看操作生产者命令参数

     bin/kafka-console-producer.sh
    
    参数描述
    –bootstrap-server<String: server toconnect to>连接的 Kafka Broker 主机名称和端口号
    –topic <String: topic>操作的 topic 名称
  2. 发送消息

    bin/kafka-console-producer.sh --bootstrap-server hadoop102:9092 --topic first
    >gun
    bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic first
    

消费者命令行操作

  1. 查看操作消费者命令参数

    bin/kafka-console-consumer.sh
    
    参数描述
    –bootstrap-server<String: server toconnect to>连接的 Kafka Broker 主机名称和端口号
    –topic <String: topic>操作的 topic 名称
    –from-beginning从头开始消费。
    –group <String: consumer group id>指定消费者组名称。
  2. 消费消息

    • 消费 first 主题中的数据

      bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic first
      
    • 把主题中所有的数据都读取出来(包括历史数据)

      bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --from-beginning --topic first
      

Kafka 生产者

在消息发送的过程中,涉及到了两个线程——main线程和 Sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator(缓冲队列)。main 线程将消息发送给 RecordAccumulator,Sender 线程不断从 RecordAccumulator 中拉取消息发送到 Kafka Broker

kafka 有自己的序列化器,main线程在内存完成

分区器决定数据分发到哪个区,一个分区一个缓冲队列,总大小32m

[外链图片转存中…(img-62BSiEIz-1705932725615)]

  • batch.size:只有数据积累到batch.size之后,sender才会发送数据。默认16k
  • linger.ms:如果数据迟迟未达到batch.size,sender等待linger.ms设置的时间到了之后就会发送数据。单位ms,默认值是0ms,表示没有延迟
  • 应答acks
    • 0:生产者发送过来的数据,不需要等数据落盘应答
    • 1:生产者发送过来的数据,Leader收到数据后应答
    • -1(all):生产者发送过来的数据,Leader和Sender队列里面的所有节点收齐数据后应答。-1和all等价

生产者重要参数列表

参数描述
bootstrap.server生产者连接集群所需的 broker 地 址 清 单 。 例 如hadoop102:9092,hadoop103:9092,hadoop104:9092,可以设置 1 个或者多个,中间用逗号隔开。注意这里并非需要所有的 broker 地址,因为生产者从给定的 broker信息
key.serilaizer和value.serializer指定发送消息的 key 和 value 的序列化类型。一定要写全类名
buffer.memoryRecordAccmulator缓冲区总大小,默认32m
batch.size缓冲区一批数据最大值,默认 16k。适当增加该值,可以提高吞吐量,但是如果该值设置太大,会导致数据传输延迟增加。
linger.ms如果数据迟迟未达到 batch.size,sender 等待 linger.time之后就会发送数据。单位 ms,默认值是 0ms,表示没有延迟。生产环境建议该值大小为 5-100ms 之间。
acks0:生产者发送过来的数据,不需要等数据落盘应答。1:生产者发送过来的数据,Leader 收到数据后应答。-1(all):生产者发送过来的数据,Leader+和 isr 队列里面的所有节点收齐数据后应答。默认值是-1,-1 和all是等价的
max.in.flight.requests.per.connection允许最多没有返回 ack 的次数,默认为 5,开启幂等性要保证该值是 1-5 的数字
retries当消息发送出现错误的时候,系统会重发消息。retries表示重试次数。默认是 int 最大值,2147483647。如果设置了重试,还想保证消息的有序性,需要设置MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION=1否则在重试此失败消息的时候,其他的消息可能发送成功了
retry.backoff.ms两次重试之间的时间间隔,默认是 100ms
enable.idempotence是否开启幂等性,默认 true,开启幂等性
compression.type生产者发送的所有数据的压缩方式。默认是 none,也就是不压缩,支持压缩类型:none、gzip、snappy、lz4 和 zstd

异步发送API

导入依赖

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>3.0.0</version>
</dependency>
普通异步发送——不带回调函数

需求:创建 Kafka 生产者,采用异步的方式发送到 Kafka Broker

public class CustomProducer {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // key,value 序列化(必须):key.serializer,value.serializer
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first","atguigu " + i));
        }

        // 5. 关闭资源
        kafkaProducer.close();
    }
}

带回调函数的异步发送

回调函数会在 producer 收到 ack 时调用,为异步调用,该方法有两个参数,分别是元数据信息(RecordMetadata)和异常信息(Exception),如果 Exception 为 null,说明消息发送成功,如果 Exception 不为 null,说明消息发送失败

[外链图片转存中…(img-6AVN4GKN-1705932725615)]

注意:消息发送失败会自动重试,不需要我们在回调函数中手动重试

public class CustomProducerCallback {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // key,value 序列化(必须):key.serializer,value.serializer\
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first", "atguigu " + i), new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception e) {
                    // 该方法在 Producer 收到 ack 时调用,为异步调用
                    if (e == null) {
                        // 没有异常,输出信息到控制台
                        System.out.println(" 主题: " +
                                metadata.topic() + "->" + "分区:" + metadata.partition());
                    } else {
                        // 出现异常打印
                        e.printStackTrace();
                    }
                }
            });
            // 延迟一会会看到数据发往不同分区
            Thread.sleep(2);
        }

        // 5. 关闭资源
        kafkaProducer.close();
    }
}

在 hadoop102 上开启 Kafka 消费者,查看是否收到消息

bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic first

同步发送 API

只需在异步发送的基础上,再调用一下 get()方法即可。

public class CustomProducerSync {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // key,value 序列化(必须):key.serializer,value.serializer\
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            // 异步发送 默认
            kafkaProducer.send(new ProducerRecord<>("first","atguigu " + i));
            // 同步发送
            kafkaProducer.send(new ProducerRecord<>("first","kafka " + i)).get();
        }

        // 5. 关闭资源
        kafkaProducer.close();
    }
}

生产者分区

  1. 便于合理使用存储资源,每个Partition在一个Broker上存储,可以把海量的数据按照分区切割成一块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果
  2. 提高并行度,生产者可以以分区为单位发送数据;消费者可以以分区为单位进行消费数据
生产者发送消息的分区策略

在 IDEA 中 ctrl +n,全局查找 DefaultPartitioner

[外链图片转存中…(img-ByJzmQV6-1705932725616)]

案例一:

将数据发往指定 partition 的情况下,例如,将所有数据发往分区 1 中。

public class CustomProducerCallbackPartitions {
    public static void main(String[] args) {
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // key,value 序列化(必须):key.serializer,value.serializer\
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            // 指定数据发送到 1 号分区,key 为空(IDEA 中 ctrl + p 查看参数)
            kafkaProducer.send(new ProducerRecord<>("first", 1,"","atguigu " + i), new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception e) {
                    if(e==null){
                        System.out.println(" 主题: " +
                                metadata.topic() + "->" + "分区:" + metadata.partition());
                    }else {
                        e.printStackTrace();
                    }
                }
            });
        }

        // 5. 关闭资源
        kafkaProducer.close();
    }
}
案例二:

没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值。

public class CustomProducerCallback {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // key,value 序列化(必须):key.serializer,value.serializer\
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            // 依次指定 key 值为 a,b,f ,数据 key 的 hash 值与 3 个分区求余,分别发往 1、2、0
            kafkaProducer.send(new ProducerRecord<>("first", "a","atguigu " + i), new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception e) {
                    // 该方法在 Producer 收到 ack 时调用,为异步调用
                    if (e == null) {
                        // 没有异常,输出信息到控制台
                        System.out.println(" 主题: " +
                                metadata.topic() + "->" + "分区:" + metadata.partition());
                    } else {
                        // 出现异常打印
                        e.printStackTrace();
                    }
                }
            });
            // 延迟一会会看到数据发往不同分区
            Thread.sleep(2);
        }

        // 5. 关闭资源
        kafkaProducer.close();
    }
}
自定义分区器

如果研发人员可以根据企业需求,自己重新实现分区器。

需求

例如我们实现一个分区器实现,发送过来的数据中如果包含 atguigu,就发往 0 号分区,不包含 atguigu,就发往 1 号分区

步骤

  1. 定义类实现 Partitioner 接口。

    /**
     * 1. 实现接口 Partitioner
     * 2. 实现 3 个方法:partition,close,configure
     * 3. 编写 partition 方法,返回分区号
     */
    public class MyPartitioner implements Partitioner {
        /**
         *  返回信息对应的分区
         *  * @param topic 主题
         *  * @param key 消息的 key
         *  * @param keyBytes 消息的 key 序列化后的字节数组
         *  * @param value 消息的 value
         *  * @param valueBytes 消息的 value 序列化后的字节数组
         *  * @param cluster 集群元数据可以查看分区信息
         *  * @return
         * */
        @Override
        public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
            // 获取消息
            String msgValue = value.toString();
            // 创建 partition
            int partition;
            // 判断消息是否包含 atguigu
            if (msgValue.contains("atguigu")){
                partition = 0;
            }else {
                partition = 1;
            }
            // 返回分区号
            return partition;
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void configure(Map<String, ?> map) {
    
        }
    }
    
  2. 重写 partition()方法

    package com.kafka;
    
    import org.apache.kafka.clients.producer.*;
    
    import java.util.Properties;
    
    public class CustomProducerCallbackPartitions {
        public static void main(String[] args) {
            // 1. 创建 kafka 生产者的配置对象
            Properties properties = new Properties();
            // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
            properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");
    
            // key,value 序列化(必须):key.serializer,value.serializer\
            properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                    "org.apache.kafka.common.serialization.StringSerializer");
            properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                    "org.apache.kafka.common.serialization.StringSerializer");
            // 添加自定义分区器
            properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,"com.atgui gu.kafka.producer.MyPartitioner");
    
            // 3. 创建 kafka 生产者对象
            KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
    
            // 4. 调用 send 方法,发送消息
            for (int i = 0; i < 5; i++) {
                // 指定数据发送到 1 号分区,key 为空(IDEA 中 ctrl + p 查看参数)
                kafkaProducer.send(new ProducerRecord<>("first", 1,"","atguigu " + i), new Callback() {
                    @Override
                    public void onCompletion(RecordMetadata metadata, Exception e) {
                        if(e==null){
                            System.out.println(" 主题: " +
                                    metadata.topic() + "->" + "分区:" + metadata.partition());
                        }else {
                            e.printStackTrace();
                        }
                    }
                });
            }
    
            // 5. 关闭资源
            kafkaProducer.close();
        }
    }
    
生产经验——生产者如何提高吞吐量

[外链图片转存中…(img-nVFD9Ne4-1705932725616)]

增加批次大小和等待时间,也会增加延迟

public class CustomProducerParameters {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");
        // key,value 序列化(必须):key.serializer,value.serializer
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");

        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        // batch.size:批次大小,默认 16K
        properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        // linger.ms:等待时间,默认 0
        properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
        // RecordAccumulator:缓冲区大小,默认 32M:buffer.memory
        properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG,33554432);

        // compression.type:压缩,默认 none,可配置值 gzip、snappy、lz4 和 zstd
        properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"snappy");
        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first","atguigu " + i));
        }
        // 5. 关闭资源
        kafkaProducer.close();
    }
}
生产经验——数据可靠性

ack应答原理

isr是所有能跟上leader同步的副本的一个集合

[外链图片转存中…(img-Yag8LLdu-1705932725616)]

[外链图片转存中…(img-uvLdmGYc-1705932725617)]

public class CustomProducerAck {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息:bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // key,value 序列化(必须):key.serializer,value.serializer
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        // 设置 acks 0,1,all
        properties.put(ProducerConfig.ACKS_CONFIG, "all");
        // 重试次数 retries,默认是 int 最大值,2147483647
        properties.put(ProducerConfig.RETRIES_CONFIG, 3);
        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        // 4. 调用 send 方法,发送消息
        for (int i = 0; i < 5; i++) {
            kafkaProducer.send(new ProducerRecord<>("first","atguigu " + i));
        }
        // 5. 关闭资源
        kafkaProducer.close();
    }
}

数据完全可靠条件 = ACK级别设置为-1 + **分区副本大于等于2 **+ISR里应答的最小副本数量大于等于2

可靠性总结:

  • acks=0,生产者发送过来数据就不管了,可靠性差,效率高;
  • acks=1,生产者发送过来数据Leader应答,可靠性中等,效率中等;
  • acks=-1,生产者发送过来数据Leader和ISR队列里面所有Follwer应答,可靠性高,效率低;

在生产环境中,

  • acks=0很少使用,
  • acks=1,一般用于传输普通日志,允许丢个别数据;
  • acks=-1,一般用于传输和钱相关的数据对可靠性要求比较高的场景。

生产经验——数据去重

收到数据同步完,leader挂了,重新选leader接收数据,应答,两份数据

[外链图片转存中…(img-W4vyJX8U-1705932725618)]

  • 至少一次(at least once)=ACK级别设置为-1 + **分区副本大于等于2 **+ISR里应答的最小副本数量大于等于2

  • 最多一次(at most once)=ack级别设置为0

  • 总结

    at least once可以保证数据不丢失,但是不能保证数据不重复

    at Most once可以保证数据不重复,但是不能保证数据不丢失

  • 精确一次(exactly once):对于一些非常重要的信息,比如和钱相关的数据,要求数据既不能重复也不丢失。

  • Kafka 0.11版本以后,引入了一项重大特性:幂等性和事务

幂等性

PID 生产者ID号

幂等性就是指Producer不论向Broker发送多少次重复数据,Broker端都只会持久化一条,保证了不重复。

精确一次(Exactly Once) = 幂等性 + 至少一次(ack=-1 + 分区副本数>=2 + ISR最小副本数量>=2)

重复数据的判断标准:具有**<PID, Partition, SeqNumber>相同主键的消息提交时,Broker只会持久化一条。其 中PID是Kafka每次重启都会分配一个新的;Partition 表示分区号;Sequence Number是单调自增的**。

所以幂等性只能保证的是在单分区单会话内不重复

[外链图片转存中…(img-QSUGZFEc-1705932725618)]

  • 开启参数 enable.idempotence 默认为 true,false 关闭
生产者事务——开启事务,必须开启幂等性

[外链图片转存中…(img-VOGNNkmh-1705932725618)]

Kafka的事务一共有如下5个API

// 1 初始化事务
void initTransactions();
// 2 开启事务
void beginTransaction() throws ProducerFencedException;
// 3 在事务内提交已经消费的偏移量(主要用于消费者)
void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets,
 String consumerGroupId) throws ProducerFencedException;
// 4 提交事务
void commitTransaction() throws ProducerFencedException;
// 5 放弃事务(类似于回滚事务的操作)
void abortTransaction() throws ProducerFencedException;

单个 Producer,使用事务保证消息的仅一次发送

public class CustomProducerTransactions {
    public static void main(String[] args) throws Exception{
        // 1. 创建 kafka 生产者的配置对象
        Properties properties = new Properties();
        // 2. 给 kafka 配置对象添加配置信息
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092");

        // key,value 序列化
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        // 设置事务 id(必须),事务 id 任意起名
        properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "transaction_id_0");
        // 3. 创建 kafka 生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        // 初始化事务
        kafkaProducer.initTransactions();
        // 开启事务
        kafkaProducer.beginTransaction();
        try {
            // 4. 调用 send 方法,发送消息
            for (int i = 0; i < 5; i++) {
                // 发送消息
                kafkaProducer.send(new ProducerRecord<>("first", "atguigu " + i));
            }
            // int i = 1 / 0;
            // 提交事务
            kafkaProducer.commitTransaction();
        } catch (Exception e) {
            // 终止事务
            kafkaProducer.abortTransaction();
        } finally {
            // 5. 关闭资源
            kafkaProducer.close();
        }
    }
}

生产者——数据有序

[外链图片转存中…(img-2CTH7tVr-1705932725618)]

生产经验——数据乱序

数据落盘之前排序

[外链图片转存中…(img-iHPNG7hW-1705932725619)]

Broker

启动zookeeper客户端

bin/zkCli.sh

通过ls命令可以查看kafka相关信息

ls /kafka
ls /kafka/brokers/ids

prettyzoo 工具

controller 辅助leader选举

[外链图片转存中…(img-qtWD8oYg-1705932725619)]

感觉老师讲的有问题,我的理解是选出broker的controller其实是在zk上创建一个controller的节点,controller是决定每个分区副本的选举

总体工作流程

[外链图片转存中…(img-60kOvMsW-1705932725619)]

  1. broker 启动后在zk中注册
  2. controller 谁先注册,谁说了算
  3. 由选举出来的Controller,监听brokers节点变化
  4. Controller 决定 Leader选举
  5. Controller 将节点上传到Zk
  6. 其他controller从zk同步相关信息
  7. 假设Broker1中leader挂了
  8. Controller 监听到节点变化
  9. 获取ISR
  10. 选举新的Leader(在isr中存活为前提,按照AR中排在前面的优先)
  11. 更新Leader及ISR

Boker 重要参数

参数名称描述
replica.lag.time.max.msISR 中,如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。该时间阈值,默认 30s
auto.leader.rebalance.enable默认是 true。 自动 Leader Partition 平衡
leader.imbalance.per.broker.percentage默认是 10%。每个 broker 允许的不平衡的 leader的比率。如果每个 broker 超过了这个值,控制器会触发 leader 的平衡
leader.imbalance.check.interval.seconds默认值 300 秒。检查 leader 负载是否平衡的间隔时间
log.segment.bytesKafka 中 log 日志是分成一块块存储的,此配置是指 log 日志划分 成块的大小,默认值 1G
log.index.interval.bytes默认 4kb,kafka 里面每当写入了 4kb 大小的日志(.log),然后就往 index 文件里面记录一个索引
log.retention.hoursKafka 中数据保存的时间,默认 7 天
log.retention.minutesKafka 中数据保存的时间,分钟级别,默认关闭。
log.retention.msKafka 中数据保存的时间,毫秒级别,默认关闭
log.retention.check.interval.ms检查数据是否保存超时的间隔,默认是 5 分钟
log.retention.bytes默认等于-1,表示无穷大。超过设置的所有日志总大小,删除最早的 segment
log.cleanup.policy默认是 delete,表示所有数据启用删除策略;如果设置值为 compact,表示所有数据启用压缩策略。
num.io.threads默认是 8。负责写磁盘的线程数。整个参数值要占总核数的 50%。
num.replica.fetchers副本拉取线程数,这个参数占总核数的 50%的 1/3
num.network.threads默认是 3。数据传输线程数,这个参数占总核数的50%的 2/3
log.flush.interval.messages强制页缓存刷写到磁盘的条数,默认是 long 的最大值,9223372036854775807。一般不建议修改,交给系统自己管理
log.flush.interval.ms每隔多久,刷数据到磁盘,默认是 null。一般不建议修改,交给系统自己管理

生产经验——节点服役和退役

新节点准备

拷贝别人的机器,要把datas和logs删除,因为它包含集群机器的唯一标识

rm -rf datas/* logs/*

如果不删,举例104上线,105下线,

hadoop104,并右键执行克隆操作,修改 IP 地址,修改主机名称为 hadoop105

查看主题信息

bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --topic first --describe
服役新节点——增加节点减少数据存储压力

均衡 != 负载均衡

hadoop105要均衡的主题

vim topics-to-move.json
{
 "topics": [
 {"topic": "first"}
 ],
 "version": 1
}

负载均衡的计划

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --topics-to-move-json-file topics-to-move.json --broker-list "0,1,2,3" --generate

创建副本存储计划(所有副本存储在 broker0、broker1、broker2、broker3 中)

vim increase-replication-factor.json
{"version":1,"partitions":[{"topic":"first","partition":0,"replicas":[2,3,0],"log_dirs":["any","any","any"]},{"topic":"first","partition":1,"replicas":[3,0,1],"log_dirs":["any","any","any"]},{"topic":"first","partition":2,"replicas":[0,1,2],"log_dirs":["any","any","any"]}]}

执行副本存储计划

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --execute

验证

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --verify
退役旧节点——把副本的数据导入到其他节点上

要均衡的主题

vim topics-to-move.json
{
 "topics": [
 {"topic": "first"}
 ],
 "version": 1
}

负载均衡的计划

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --topics-to-move-json-file topics-to-move.json --broker-list "0,1,2" --generate

创建副本存储计划(所有副本存储在 broker0、broker1、broker2中)

vim increase-replication-factor.json

执行副本计划

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --execute

验证副本存储计划

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --verify

执行停止命令

bin/kafka-server-stop.sh

kafka 副本

  1. Kafka 副本作用:提高数据可靠性。

  2. Kafka 默认副本 1 个,生产环境一般配置为 2 个,保证数据可靠性;太多副本会增加磁盘存储空间,增加网络上数据传输,降低效率。

  3. Kafka 中副本分为:Leader 和 Follower。Kafka 生产者只会把数据发往 Leader,然后 Follower 找 Leader 进行同步数据。

  4. Kafka 分区中的所有副本统称为 AR(Assigned Repllicas)。

    AR = ISR + OSR

    ISR,表示和 Leader 保持同步的 Follower 集合。如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。该时间阈值由 replica.lag.time.max.ms 参数设定,默认 30s。Leader 发生故障之后,就会从 ISR 中选举新的 Leader

    OSR,表示 Follower 与 Leader 副本同步时,延迟过多的副本

leader 选举流程

Kafka 集群中有一个 broker 的 Controller 会被选举为 Controller Leader,负责管理集群broker 的上下线,所有 topic 的分区副本分配和 Leader 选举等工作

Controller 的信息同步工作是依赖于 Zookeeper 的

[外链图片转存中…(img-v14cTdWg-1705932725620)]创建一个新的 topic,4 个分区,4 个副本

bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --topic atguigu1 --partitions 4 --replication-factor 4

查看 Leader 分布情况

bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --describe --topic atguigu1

停止掉 hadoop105 的 kafka 进程,并查看 Leader 分区情况

bin/kafka-server-stop.sh

停止掉 hadoop104 的 kafka 进程,并查看 Leader 分区情况

启动 hadoop105 的 kafka 进程,并查看 Leader 分区情况

bin/kafka-server-start.sh -daemon config/server.properties

启动 hadoop104 的 kafka 进程,并查看 Leader 分区情况

replicas 按照顺序上位

Topic: atguigu1	Partition: 0	Leader: 0	Replicas: 0,2,3,1	Isr: 0,3,1,2
	Topic: atguigu1	Partition: 1	Leader: 3	Replicas: 2,3,1,0	Isr: 3,0,1,2
	Topic: atguigu1	Partition: 2	Leader: 3	Replicas: 3,1,0,2	Isr: 3,0,1,2
	Topic: atguigu1	Partition: 3	Leader: 0	Replicas: 1,0,2,3	Isr: 0,3,1,2

恢复进程之后

Isr 队列会增加broker(顺序按照添加顺序),leader 信息不会有变化

Leader 和 follower 故障处理细节
  • follower 故障处理细节

    [外链图片转存中…(img-NGRDKMnX-1705932725620)]

  • leader 故障处理细节

    [外链图片转存中…(img-pmUeq50k-1705932725620)]

分区副本分配

如果 kafka 服务器只有 4 个节点,那么设置 kafka 的分区数大于服务器台数,在 kafka底层如何分配存储副本呢?

创建一个新的 topic,名称为 second

bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --partitions 16 --replication-factor 3 --topic second

查看分区和副本情况。

bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --describe --topic second
Topic: second	TopicId: ZVErgaa1R7GUQvh8oVh7AQ	PartitionCount: 16	ReplicationFactor: 3   Configs: segment.bytes=1073741824
	Topic: second	Partition: 0	Leader: 3	Replicas: 3,1,0	Isr: 3,1,0
	Topic: second	Partition: 1	Leader: 1	Replicas: 1,0,2	Isr: 1,0,2
	Topic: second	Partition: 2	Leader: 0	Replicas: 0,2,3	Isr: 0,2,3
	Topic: second	Partition: 3	Leader: 2	Replicas: 2,3,1	Isr: 2,3,1
	Topic: second	Partition: 4	Leader: 3	Replicas: 3,0,2	Isr: 3,0,2
	Topic: second	Partition: 5	Leader: 1	Replicas: 1,2,3	Isr: 1,2,3
	Topic: second	Partition: 6	Leader: 0	Replicas: 0,3,1	Isr: 0,3,1
	Topic: second	Partition: 7	Leader: 2	Replicas: 2,1,0	Isr: 2,1,0
	Topic: second	Partition: 8	Leader: 3	Replicas: 3,2,1	Isr: 3,2,1
	Topic: second	Partition: 9	Leader: 1	Replicas: 1,3,0	Isr: 1,3,0
	Topic: second	Partition: 10	Leader: 0	Replicas: 0,1,2	Isr: 0,1,2
	Topic: second	Partition: 11	Leader: 2	Replicas: 2,0,3	Isr: 2,0,3
	Topic: second	Partition: 12	Leader: 3	Replicas: 3,1,0	Isr: 3,1,0
	Topic: second	Partition: 13	Leader: 1	Replicas: 1,0,2	Isr: 1,0,2
	Topic: second	Partition: 14	Leader: 0	Replicas: 0,2,3	Isr: 0,2,3
	Topic: second	Partition: 15	Leader: 2	Replicas: 2,3,1	Isr: 2,3,1

[外链图片转存中…(img-ssTJawCi-1705932725621)]

生产经验——手动调整分区副本存储

在生 产环境中,每台服务器的配置和性能不一致,但是Kafka只会根据自己的代码规则创建对应的分区副

本,就会导致个别服务器存储压力较大。所有需要手动调整分区副本的存储。

需求:创建一个新的topic,4个分区,两个副本,名称为three。将 该topic的所有副本都存储到broker0和

broker1两台服务器上

[外链图片转存中…(img-TUTmPWX7-1705932725621)]

步骤

  • 创建一个新的 topic,名称为 three

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --partitions 4 --replication-factor 2 --topic three
    
  • 查看分区副本存储情况

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --describe --topic three
    
  • 创建副本存储计划(所有副本都指定存储在 broker0、broker1 中)

    vim increase-replication-factor.json
    {
    "version":1,
    "partitions":[{"topic":"three","partition":0,"replicas":[0,1]},
    {"topic":"three","partition":1,"replicas":[0,1]},
    {"topic":"three","partition":2,"replicas":[1,0]},
    {"topic":"three","partition":3,"replicas":[1,0]}] 
    }
    
  • 执行副本存储计划

    bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --execute
    
  • 验证副本存储计划

    bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --verify
    
  • 查看分区副本存储情况

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --describe --topic three
    

生产经验——Leader Partition负载平衡

Leader Partition自动平衡

发生过节点的宕机,然后再重启

[外链图片转存中…(img-rRuiOMKP-1705932725621)]

参数名称描述
auto.leader.rebalance.enable默认是 true。 自动 Leader Partition 平衡。生产环境中,leader 重选举的代价比较大,可能会带来性能影响,建议设置为 false 关闭
leader.imbalance.per.broker.percentage默认是 10%。每个 broker 允许的不平衡的 leader的比率。如果每个 broker 超过了这个值,控制器会触发 leader 的平衡
leader.imbalance.check.interval.seconds默认值 300 秒。检查 leader 负载是否平衡的间隔时间

生产经验——增加副本因子

在生产环境当中,由于某个主题的重要等级需要提升,考虑增加副本。副本数的增加需要先制定计划,然后根据计划执行。

bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --partitions 3 --replication-factor 1 --topic four

手动增加副本存储

创建副本存储计划(所有副本都指定存储在 broker0、broker1、broker2 中)

vim increase-replication-factor.json
{"version":1,"partitions":[{"topic":"four","partition":0,"replicas":[0,1,2]},{"topic":"four","partition":1,"replicas":[0,1,2]},{"topic":"four","partition":2,"replicas":[0,1,2]}]}

执行副本存储计划

bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file increase-replication-factor.json --execute

kafka 文件存储机制

[外链图片转存中…(img-SgwFB3uT-1705932725621)]

查看hadoop102(或者 hadoop103、hadoop104)的/opt/module/kafka/datas/first-1 (first-0、first-2)路径上的文件

ls
00000000000000000000.index  00000000000000000000.timeindex  leader-epoch-checkpoint
00000000000000000000.log    00000000000000000015.snapshot   partition.metadata

通过工具查看 index 和 log 信息

kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.index
kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.log

[外链图片转存中…(img-0b8gDk51-1705932725622)]

说明:日志存储参数配置

参数描述
log.segment.bytesKafka 中 log 日志是分成一块块存储的,此配置是指 log 日志划分成块的大小,默认值 1G
log.index.interval.bytes默认 4kb,kafka 里面每当写入了 4kb 大小的日志(.log),然后就往 index 文件里面记录一个索引,稀疏索引

文件清理策略

Kafka 中默认的日志保存时间为 7 天,可以通过调整如下参数修改保存时间。

  • log.retention.hours,最低优先级小时,默认 7 天
  • log.retention.minutes,分钟。
  • log.retention.ms,最高优先级毫秒
  • log.retention.check.interval.ms,负责设置检查周期,默认 5 分钟。

那么日志一旦超过了设置的时间,怎么处理呢?

Kafka 中提供的日志清理策略有 delete 和 compact 两种

  1. delete 日志删除:将过期数据删除

    log.cleanup.policy = delete 所有数据启用删除策略

    • 基于时间:默认打开以 segment 中所有记录中的最大时间戳作为该文件时间戳
    • 基于大小:默认关闭。超过设置的所有日志总大小,删除最早的 segment。

    log.retention.bytes,默认等于-1,表示无穷大。

**思考:**如果一个 segment 中有一部分数据过期,一部分没有过期,怎么处理?

[外链图片转存中…(img-qcnN0TFi-1705932725622)]

以 segment 中所有记录中的最大时间戳作为该文件时间戳,不删除

compact 日志压缩

[外链图片转存中…(img-PRmQLgQF-1705932725622)]

高效读写数据

  1. Kafka本身是分布式集群,可以采用分区技术,并行度高
  2. 读数据采用稀疏索引,可以快速定位要消费的数据
  3. 顺序写磁盘

Kafka 的 producer 生产数据,要写入到 log 文件中,写的过程是一直追加到文件末端,为顺序写。官网有数据表明,同样的磁盘,顺序写能到 600M/s,而随机写只有 100K/s。这与磁盘的机械机构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间

[外链图片转存中…(img-pr1XlaBX-1705932725622)]

  1. 页缓存 +零拷贝技术

    **零拷贝:**Kafka的数据加工处理操作交由Kafka生产者和Kafka消费者处理。Kafka Broker应用层不关心存储的数据,所以就不用走应用层,传输效率高。

    PageCache页缓存:Kafka重度依赖底层操作系统提供的PageCache功 能。当上层有写操作时,操作系统只是将数据写入PageCache。当读操作发生时,先从PageCache中查找,如果找不到,再去磁盘中读取。实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用

    [外链图片转存中…(img-2CevRyG9-1705932725623)]

因为kafka不对数据做任何处理,所以可以不用走应用层

参数描述
log.flush.interval.messages强制页缓存刷写到磁盘的条数,默认是 long 的最大值,9223372036854775807。一般不建议修改,交给系统自己管
log.flush.interval.ms每隔多久,刷数据到磁盘,默认是 null。一般不建议修改,交给系统自己管理

消费者

[外链图片转存中…(img-2tazKImI-1705932725623)]

消费者总体工作流程

独立消费者,相互不影响,消费组不一样

[外链图片转存中…(img-U6HlnqVa-1705932725623)]

消费者组原理

Consumer Group(CG):消费者组,由多个consumer组成。形成一个消费者组的条件,是所有消费者的groupid相同。

  • 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。
  • 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者

[外链图片转存中…(img-e8P61vuN-1705932725623)]

消费者组初始化流程

50是分区数 groupid

[外链图片转存中…(img-TTWf84u3-1705932725624)]

重点:再平衡

消费者组详细消费流程

[外链图片转存中…(img-V925lgMZ-1705932725624)]

消费者重要参数

参数名称描述
bootstrap.servers向 Kafka 集群建立初始连接用到的 host/port 列表
key.deserializer 和 value.deserializer指定接收消息的 key 和 value 的反序列化类型。一定要写全类名
group.id标记消费者所属的消费者组。
enable.auto.commit默认值为 true,消费者会自动周期性地向服务器提交偏移量
auto.commit.interval.ms如果设置了 enable.auto.commit 的值为 true, 则该值定义了消费者偏移量向 Kafka 提交的频率,默认 5s
auto.offset.reset当 Kafka 中没有初始偏移量或当前偏移量在服务器中不存在(如,数据被删除了),该如何处理? earliest:自动重置偏移量到最早的偏移量。 latest:默认,自动重置偏移量为最新的偏移量。 none:如果消费组原来的(previous)偏移量不存在,则向消费者抛异常。 anything:向消费者抛异常
offsets.topic.num.partitions__consumer_offsets 的分区数,默认是 50 个分区
heartbeat.interval.msKafka 消费者和 coordinator 之间的心跳时间,默认 3s。该条目的值必须小于 session.timeout.ms,也不应该高于session.timeout.ms 的 1/3
session.timeout.msKafka 消费者和 coordinator 之间连接超时时间,默认 45s。超过该值,该消费者被移除,消费者组执行再平衡
max.poll.interval.ms消费者处理消息的最大时长,默认是 5 分钟。超过该值,该消费者被移除,消费者组执行再平衡。
fetch.min.bytes默认 1 个字节。消费者获取服务器端一批消息最小的字节
fetch.max.bytes默认 Default: 52428800(50 m)消费者获取服务器端一批消息最大的字节数。如果服务器端一批次的数据大于该值(50m)仍然可以拉取回来这批数据,因此,这不是一个绝对最大值对最大值。一批次的大小受 message.max.bytes (broker config)or max.message.bytes (topic config)影响
max.poll.records一次 poll 拉取数据返回消息的最大条数,默认是 500 条
fetch.max.wait.ms默认 500ms。如果没有从服务器端获取到一批数据的最小字节数。该时间到,仍然会返回数据

消费者API

注意:在消费者 API 代码中必须配置消费者组 id。命令行启动消费者不填写消费者组id 会被自动填写随机的消费者组 id。

**需求:**创建一个独立消费者,消费 first 主题中数据

public class CustomConsumer {
    public static void main(String[] args) {
        // 1.创建消费者的配置对象
        Properties properties = new Properties();
        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // 配置序列化 必须
        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        // 配置消费者组(组名任意起名) 必须
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
        // 创建消费者对象
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);

        // 注册要消费的主题(可以消费多个主题)
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        kafkaConsumer.subscribe(topics);

        // 拉取数据打印
        while (true) {
            // 设置 1s 中消费一批数据
            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
            // 打印消费到的数据
            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
        }
    }
}

发送数据

 bin/kafka-console-producer.sh --bootstrap-server hadoop102:9092 --topic first
独立消费者案例(订阅分区)

需求:创建一个独立消费者,消费 first 主题 0 号分区的数据

image-20220525152250050
public class CustomConsumerPartition {
    public static void main(String[] args) {
        // 1.创建消费者的配置对象
        Properties properties = new Properties();
        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // 配置序列化 必须
        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        // 配置消费者组(组名任意起名) 必须
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
        // 创建消费者对象
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);

        // 消费某个主题的某个分区数据
        ArrayList<TopicPartition> topicPartitions = new ArrayList<>();
        topicPartitions.add(new TopicPartition("first", 0));
        kafkaConsumer.assign(topicPartitions);

        // 拉取数据打印
        while (true) {
            // 设置 1s 中消费一批数据
            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
            // 打印消费到的数据
            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
        }
    }
}
消费组案例

需求:测试同一个主题的分区数据,只能由一个消费者组中的一个消费

image-20220525154123823

复制一份基础消费者的代码,在 IDEA 中同时启动,即可启动同一个消费者组中的两个消费者

生产经验——分区的分配以及再平衡

Kafka有四种主流的分区分配策略: Range、RoundRobin、Sticky、CooperativeSticky

[外链图片转存中…(img-HdCrTtD9-1705932725624)]

参数名称描述
heartbeat.interval.msKafka 消费者和 coordinator 之间的心跳时间,默认 3s。该条目的值必须小于 session.timeout.ms,也不应该高于session.timeout.ms 的 1/3
session.timeout.msKafka 消费者和 coordinator 之间连接超时时间,默认 45s。超过该值,该消费者被移除,消费者组执行再平衡。
max.poll.interval.ms消费者处理消息的最大时长,默认是 5 分钟。超过该值,该消费者被移除,消费者组执行再平衡
partition.assignment.strategy消 费 者 分 区 分 配 策 略 , 默 认 策 略 是 Range +CooperativeSticky。Kafka 可以同时使用多个分区分配策略。可 以 选 择 的 策 略 包 括 : Range 、 RoundRobin 、 Sticky 、CooperativeSticky。
Range分区策略以及再平衡

容易产生数据倾斜

[外链图片转存中…(img-yZp9T8nH-1705932725624)]

如果服务器挂掉了

Range 分区分配再平衡案例

停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。

1 号消费者:消费到 3、4 号分区数据。

2 号消费者:消费到 5、6 号分区数据。

0 号消费者的任务会整体被分配到 1 号消费者或者 2 号消费者。

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。

再次重新发送消息观看结果(45s 以后)。

1 号消费者:消费到 0、1、2、3 号分区数据。

2 号消费者:消费到 4、5、6 号分区数据。

说明:消费者 0 已经被踢出消费者组,所以重新按照 range 方式分配

RoundRoin 分区策略以及再平衡

RoundRobin 分区策略原理

[外链图片转存中…(img-SF1FKgY6-1705932725625)]

// 修改分区分配策略
properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.RoundRobinAssignor");

RoundRobin 分区分配再平衡案例

(1)停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。

1 号消费者:消费到 2、5 号分区数据

2 号消费者:消费到 4、1 号分区数据

0 号消费者的任务会按照 RoundRobin 的方式,把数据轮询分成 0 、6 和 3 号分区数据,分别由 1 号消费者或者 2 号消费者消费。

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。

(2)再次重新发送消息观看结果(45s 以后)。

1 号消费者:消费到 0、2、4、6 号分区数据

2 号消费者:消费到 1、3、5 号分区数据

说明:消费者 0 已经被踢出消费者组,所以重新按照 RoundRobin 方式分配

Sticky分区策略 以及再平衡

**粘性分区定义:**可以理解为分配的结果带有“粘性的”。即在执行一次新的分配之前,考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销。

粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化。

需求

设置主题为 first,7 个分区;准备 3 个消费者,采用粘性分区策略,并进行消费,观察消费分配情况。然后再停止其中一个消费者,再次观察消费分配情况。

步骤

修改分区分配策略为粘性。

注意:3 个消费者都应该注释掉,之后重启 3 个消费者,如果出现报错,全部停止等会再重启,或者修改为全新的消费者组

// 修改分区分配策略
ArrayList<String> startegys = new ArrayList<>();
startegys.add("org.apache.kafka.clients.consumer.StickyAssignor");
properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, startegys);

Sticky 分区分配再平衡案例

(1)停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。

1 号消费者:消费到 2、5、3 号分区数据。

2 号消费者:消费到 4、6 号分区数据。

0 号消费者的任务会按照粘性规则,尽可能均衡的随机分成 0 和 1 号分区数据,分别由 1 号消费者或者 2 号消费者消费。

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。

(2)再次重新发送消息观看结果(45s 以后)。

1 号消费者:消费到 2、3、5 号分区数据。

2 号消费者:消费到 0、1、4、6 号分区数据。

说明:消费者 0 已经被踢出消费者组,所以重新按照粘性方式分配

offset 的默认维护位置

[外链图片转存中…(img-ZjUUupyt-1705932725625)]

__consumer_offsets 主题里面采用 key 和 value 的方式存储数据。key 是 group.id+topic+分区号,value 就是当前 offset 的值。每隔一段时间,kafka 内部会对这个 topic 进行compact,也就是每个 group.id+topic+分区号就保留最新数据。

消费offset 案例

思想:__consumer_offsets 为 Kafka 中的 topic,那就可以通过消费者进行消费。

  • 在配置文件 config/consumer.properties 中添加配置 exclude.internal.topics=false,默认是 true表示不能消费系统主题。为了查看该系统主题数据,所以该参数修改为 false
  • 采用命令行方式,创建一个新的 topic。
bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --topic atguigu --partitions 2 --replication-factor 2
  • 启动生产者往 atguigu 生产数据

    bin/kafka-console-producer.sh --topic atguigu --bootstrap-server hadoop102:9092
    
  • 启动消费者消费 atguigu 数据

    bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic atguigu --group test
    

    注意:指定消费者组名称,更好观察数据存储位置(key 是 group.id+topic+分区号)

  • 查看消费者消费主题__consumer_offsets

    bin/kafka-console-consumer.sh --topic __consumer_offsets --bootstrap-server hadoop102:9092 --consumer.config config/consumer.properties --formatter 
    "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --from-beginning
    

自动提交 offset

[外链图片转存中…(img-QvdJ9veM-1705932725625)]

参数名称描述
enable.auto.commit默认值为 true,消费者会自动周期性地向服务器提交偏移量
auto.commit.interval.ms如果设置了 enable.auto.commit 的值为 true, 则该值定义了消费者偏移量向 Kafka 提交的频率,默认 5s

手动提交offset

虽然自动提交offset十分简单便利,但由于其是基于时间提交的,开发人员难以把握offset提交的时机。因 此Kafka还提供了手动提交offset的API。

手动提交offset的方法有两种:分别是commitSync(同步提交)和commitAsync(异步提交)。两者的相同点是,都会将**本次提交的一批数据最高的偏移量提交;**不同点是,同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故有可能提交失败。

  • commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。
  • commitAsync(异步提交) :发送完提交offset请求后,就开始消费下一批数据了。
同步提交

由于同步提交 offset 有失败重试机制,故更加可靠,但是由于一直等待提交结果,提交的效率比较低。以下为同步提交 offset 的示例

		// 是否自动提交 offset
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);

        while (true) {
            // 同步提交 offset
            consumer.commitSync();
        }
异步提交

虽然同步提交 offset 更可靠一些,但是由于其会阻塞当前线程,直到提交成功。因此吞吐量会受到很大的影响。因此更多的情况下,会选用异步提交 offset 的方式

// 是否自动提交 offset
 properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
//5. 消费数据
 while (true){
 	// 异步提交 offset
 	consumer.commitAsync();
 }
指定offset 消费

auto.offset.reset = earliest | latest | none 默认是 latest

当 Kafka 中没有初始偏移量(消费者组第一次消费)或服务器上不再存在当前偏移量时(例如该数据已被删除),该怎么办?

  • earliest:自动将偏移量重置为最早的偏移量,–from-beginning

  • **latest(默认值):**自动将偏移量重置为最新偏移量

  • **none:**如果未找到消费者组的先前偏移量,则向消费者抛出异常、

    image-20220525221005367

任意指定 offset 位移开始消费

package com.kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

public class CustomConsumerSeek {
    public static void main(String[] args) {
        // 1.创建消费者的配置对象
        Properties properties = new Properties();
        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // 配置序列化 必须
        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        // 配置消费者组(组名任意起名) 必须
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test2");

        // 创建消费者对象
        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);

        // 注册要消费的主题(可以消费多个主题)
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        consumer.subscribe(topics);

        Set<TopicPartition> assignment= new HashSet<>();
        while (assignment.size() == 0) {
            consumer.poll(Duration.ofSeconds(1));
            // 获取消费者分区分配信息(有了分区分配信息才能开始消费)
            assignment = consumer.assignment();
        }
        // 遍历所有分区,并指定 offset 从 100 的位置开始消费
        for (TopicPartition tp: assignment) {
            consumer.seek(tp, 200);
        }

        // 拉取数据打印
        while (true) {
            // 设置 1s 中消费一批数据
            ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));
            // 打印消费到的数据
            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
            // 同步提交 offset
            consumer.commitSync();
        }
    }
}

注意:每次执行完,需要修改消费者组名;??

指定时间消费

需求:在生产环境中,会遇到最近消费的几个小时数据异常,想重新按照时间消费。例如要求按照时间消费前一条的数据,怎么处理

public class CustomConsumerForTime {
    public static void main(String[] args) {
        // 1.创建消费者的配置对象
        Properties properties = new Properties();
        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        // 配置序列化 必须
        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        // 配置消费者组(组名任意起名) 必须
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test2");

        // 创建消费者对象
        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);

        // 注册要消费的主题(可以消费多个主题)
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        consumer.subscribe(topics);

        Set<TopicPartition> assignment= new HashSet<>();
        while (assignment.size() == 0) {
            consumer.poll(Duration.ofSeconds(1));
            // 获取消费者分区分配信息(有了分区分配信息才能开始消费)
            assignment = consumer.assignment();
        }
        HashMap<TopicPartition, Long> timestampToSearch = new HashMap<>();
        // 封装集合存储,每个分区对应一天前的数据
        for (TopicPartition topicPartition : assignment) {
            timestampToSearch.put(topicPartition, System.currentTimeMillis() - 1 * 24 * 3600 * 1000);
        }
        // 获取从 1 天前开始消费的每个分区的 offset
        Map<TopicPartition, OffsetAndTimestamp> offsets = consumer.offsetsForTimes(timestampToSearch);

        // 遍历每个分区,对每个分区设置消费时间。
        for (TopicPartition topicPartition : assignment) {
            OffsetAndTimestamp offsetAndTimestamp = offsets.get(topicPartition);
            // 根据时间指定开始消费的位置
            if (offsetAndTimestamp != null){
                consumer.seek(topicPartition, offsetAndTimestamp.offset());
            }
        }

        // 拉取数据打印
        while (true) {
            // 设置 1s 中消费一批数据
            ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));
            // 打印消费到的数据
            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
            // 同步提交 offset
            consumer.commitSync();
        }
    }
}

漏消费和重复消费

  • 重复消费:已经消费了数据,但是offset没提交

  • 漏消费:先提交offset后消费,有可能造成数据的漏消费

[外链图片转存中…(img-bU7qIP10-1705932725626)]

思考:怎么能做到既不漏消费也不重复消费呢?详看消费者事务

生产经验——消费者事务

[外链图片转存中…(img-ZDwZUC7D-1705932725634)]

下游也要支持事务

生产经验——数据积压(消费者如何提高吞吐量)

[外链图片转存中…(img-hUgXk9hV-1705932725634)]

增加完条数,50M大小也要增加

参数名称描述
fetch.max.bytes默认5242880(50m)。消费者获取服务端一批消息最大的字节数如果服务端一批次的数据大于该值(50m)仍然可以拉取回来这批数据,因此,这不是一个绝对最大值。一批次的大小**message.max.bytes(broker config) or max.message.bytes(topic config)**影响
max.poll.records一次poll拉取数据返回消息的最大条数,默认500条

kafka-eagle 监控

关闭集群

kf.sh stop

kafka-eagle 框架可以监控kafka集群的整体运行情况,在生产环境中经常使用

vim bin/kafka-server-start.sh
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
 export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
 export KAFKA_HEAP_OPTS="-server -Xms2G -Xmx2G -
XX:PermSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -
XX:ParallelGCThreads=8 -XX:ConcGCThreads=5 -
XX:InitiatingHeapOccupancyPercent=70"
 export JMX_PORT="9999"
 #export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi

注意:修改之后在启动Kafka之前要分发之其他节点

 xsync kafka-server-start.sh

安装

上传压缩包kafka-eagle-bin-2.0.8.tar.gz 到集群/opt/software目录

解压

tar -zxvf kafka-eagle-bin-2.0.8.tar.gz
tar -zxvf efak-web-2.0.8-bin.tar.gz -C /opt/module/

修改名称

mv efak-web-2.0.8/ efak

修改配置文件 /opt/module/efak/conf/system-config.properties

######################################
# multi zookeeper & kafka cluster list
# Settings prefixed with 'kafka.eagle.' will be deprecated, use 'efak.' 
instead
######################################
efak.zk.cluster.alias=cluster1
cluster1.zk.list=hadoop102:2181,hadoop103:2181,hadoop104:2181/kafka\

######################################
# kafka offset storage
######################################
# offset 保存在 kafka
cluster1.efak.offset.storage=kafka
######################################
# 配置 mysql 连接
efak.driver=com.mysql.jdbc.Driver
efak.url=jdbc:mysql://hadoop102:3306/ke?useUnicode=true&characterEncoding=UT
F-8&zeroDateTimeBehavior=convertToNull
efak.username=root
efak.password=000000

添加环境变量

vim /etc/profile.d/my_env.sh
# kafkaEFAK
export KE_HOME=/opt/module/efak
export PATH=$PATH:$KE_HOME/bin

注意:source /etc/profile

启动之前需要先启动zk以及kafka

 kf.sh start
 bin/ke.sh start

如果停止efak,执行命令

 bin/ke.sh stop

kafka-eagle 页面操作

http://192.168.10.102:8048/

[外链图片转存中…(img-bC1g9Ho7-1705932725635)]\

kafka-kraft 模式

[外链图片转存中…(img-uvuTqvgk-1705932725635)]

左图为kafka现有架构,元数据在zookeeper中,运行时动态选举controller进行kafka集群管理。右图kafka模式架构(实验性),不再依赖zookeeper集群,而是controller节点代替zookeeper,元数据保存在controller中,由controller直接进行集群管理

kafka-kraft 集群部署

解压一份kafka安装包

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

重命名为kafka2

在hadoop102 上修改/opt/module/kafka2/config/kraft/server.properties 配置

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

分发kafka2

xsync kafka2/

在hadoop103 和 hadoop104 上需要对node.id相应改变,值需要和controller.quorum.voters 对应

在hadoop103 和 hadoop104 上需要根据各自的主机名称,修改相应的advertised.Listensers地址

初始化集群数据目录

首先生成存储目录唯一Id

bin/kafka-storage.sh random-uuidJ7s9e8PPTKOO47PxzI39VA

用该ID格式化kafka存储目录(三台节点)

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

启动kafka集群

bin/kafka-server-start.sh -daemon config/kraft/server.properties
bin/kafka-server-start.sh -daemon config/kraft/server.properties
bin/kafka-server-start.sh -daemon config/kraft/server.properties

停止kafka集群

bin/kafka-server-stop.sh
bin/kafka-server-stop.sh
bin/kafka-server-stop.sh

kafka-kraft 集群启动停止脚本

在/home/atguigu/bin 目录下创建文件 kf2.sh 脚本文件

vim kf2.sh

脚本如下:

#! /bin/bash
case $1 in
"start"){
 for i in hadoop102 hadoop103 hadoop104
 do
 echo " --------启动 $i Kafka2-------"
 ssh $i "/opt/module/kafka2/bin/kafka-server-start.sh -
daemon /opt/module/kafka2/config/kraft/server.properties"
 done
};;
"stop"){
 for i in hadoop102 hadoop103 hadoop104
 do
 echo " --------停止 $i Kafka2-------"
 ssh $i "/opt/module/kafka2/bin/kafka-server-stop.sh "
 done
};;
esac

添加执行权限

chmod +x kf2.sh
  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值