01-kafka

1. 什么是kafka

  • kafka的来源

  • kafka的外在表现

  • kafka的作者和市场情况

2. kafka的基本概念

  • 消息和批次

消息:相当于数据表中的一条记录,只接受字节数组,send:message{key(负责路由分配),value(byte [])}
批次:kafka发送消息是批次发送,后期调优是批量的大小和延迟,即批次和吞吐量

  • 主题和分区

主题:是kafka的逻辑分配单元,比如数据库的表单,即kafka的数据发送到相应的主题中

分区:一个主题中包含多个分区(Parttion),分区是kafka物理存储单位,可以保证顺序存储,先进先出

注意:分区级别可以确保顺序,但是主题级别是无顺序,因为数据可以存在不一定哪个分区

  • 生产者和消费者

生产者:可以将消息负载均衡的发送到一个主题中的不同分区,但是如何想叫消息有顺序的话可以将消息发送到一个主题中的固定分区中

消费者:消费者在一个分区中消息也具备顺序性

  • 偏移量和消费者群组

偏移量:消息在发送的时候分区里边的消息比如十条消息偏移量是九,在消费的时候消费偏移量是消费者在分区消费消息时消费到的位置,因为消息是持久化的,这样就可以防止消息重复消费

消费者群组:多个消费者可以组成一个消费者群组,一个消费者群组里一个分区只能匹配群组里边的一个消费者,但是一个消费者可以消费多个分区

疑问:消费者组每一个消费者绑定分区是绑定死的还是每次绑定都是不一样的

  • Broker和集群

Broker:一台独立的kafka服务

集群:把多台Broker集合在一起构建成集群

  • 复制与首领

复制:为了保证数据不丢失创建副本,但是不知道主次关系,所以有首领概念

首领:每个分区选择不同Broker当作首领这样的话可以保证数据最大化保护,生产和消费都是通过首领进行操作,副本是保存数据,不会丢失数据,提高容错

  • 副本机制

只是走首领副本保存数据

3. kafka版本

4. kafka安装

前提:安装响应版本的jdk

4.1 2.x的安装、管理和配置

  • 下载

网址

  • windows安装
bash 复制代码
#打开kafka安装的目录cmd首先启动zookeeper要配置响应的配置文件
kafka_2.12-2.8.1\kafka_2.12-2.8.1\bin\windows>zookeeper-server-start.bat ..\..\config/zookeeper.properties
bash 复制代码
#启动kafka-server的启动文件,也是启动程序和相应的配置文件
kafka_2.12-2.8.1\kafka_2.12-2.8.1\bin\windows>kafka-server-start.bat ..\..\config/server.properties
  • linux安装
bash 复制代码
#后台启动 zookeeper(默认端口2181)
nohup bin/zookeeper-server-start.sh config/zookeeper.properties 1>zookeeper_stdout.log 2>zookeeper_stderr.log &
bash 复制代码
# 后台启动 kafka-server
nohup bin/kafka-server-start.sh config/server.properties > kafka.log 2>&1 &
  • kafka基本操作和管理
bash 复制代码
# 主题相关
./kafka-topics.sh --bootstrap-server localhost:9092 --list
./kafka-topics.sh --bootstrap-server localhost:9092 --describe
# 创建三个分区、副本数为1、主题名称为new-topic
./kafka-topics.sh --bootstrap-server localhost:9092 --create --topic new-topic --partitions 3 --replication-factor 1
bash 复制代码
# 查看消费者偏移量详情
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group my-group --describe
bash 复制代码
# 使用bootstrap-server
./kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic my-topic
bash 复制代码
#创建生产者
./kafka-console-producer.sh --broker-list localhost:9092 --topic my-topic
bash 复制代码
# 查看最新消息
./kafka-console-consumer.sh --bootstrap-sever localhost:9092 --topic my-topic
bash 复制代码
# 从头消费
./kafka-console-consumer.sh --bootstrap-sever localhost:9092 --topic my-topic --from -beginning
bash 复制代码
# 消费特定分区
./kafka-console-consummer.sh --bootstrap-server localhsot:9092 --topic my-topic --partition 0
bash 复制代码
# 消费特定偏移量
./kafka-console-consummer.sh --bootstrap-server localhsot:9092 --topic my-topic --offset 100 --partition 0
bash 复制代码
#查看主题配置
./kafka-configs.sh --zookeeper localhost:2181 --entity-type topics --entity-name new-topic --describe
bash 复制代码
# 修改主题配置(如修改保留时间)
./kafka-configs.sh --zookeeper localhost:2181 --entity-type topics --entity-name new-topic --alter --add -config retention,
  • Broker配置项
properties 复制代码
############################# Server Basics #############################

# broker id 如何是集群的话要是不唯一的正数
broker.id=0

############################# Log Basics #############################

# 持久化文件存放位置
log.dirs=/tmp/kafka-logs


############################# Zookeeper #############################

# Zookeeper connection string (see zookeeper docs for details).
# This is a comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
# You can also append an optional chroot string to the urls to specify the
# root directory for all kafka znodes.
# zookeeper的地址
zookeeper.connect=localhost:2181

4.2 3.x的安装、管理和配置

  • 下载(对比2.x多一个KRaft模式)

kafka 3.9.1推荐jdk版本17

  • windows安装

和上边2.x启动一致,启动zookeeper和kafka

bash 复制代码
#KRaft模式启动
# 会生成一个唯一的id区别不同的kafka集群
kafka-storage.bat random-uuid
kafka-storage.bat format --config ..\..\config\kraft\server.properties --cluster-id 上一步生成的uuid
#启动kafka 就不用在启动zookeeper
kafka-server-start.bat ..\..\config/server.properties
  • linux安装

和上边2.x启动一致,启动zookeeper和kafka

bash 复制代码
#KRaft模式启动
# 会生成一个唯一的id区别不同的kafka集群
kafka-storage.bat random-uuid
kafka-storage.bat format --config ..\..\config\kraft\server.properties --cluster-id 上一步生成的uuid
#启动kafka 就不用在启动zookeeper
kafka-server-start.bat ..\..\config\kraft\server.properties
  • kafka基本的操作和管理(单机非集群)

和2.x一致命令

  • Broker配置项

和2.x差不多但是使用kraft模式需要注意一下:\kafka_2.12-3.9.1\config\kraft\server.properties

properties 复制代码
############################# Server Basics #############################

# 定义节点角色
#	broker:消息代理,生产/消费 请求
# 	controller:集群控制器,管理元数据和领导选举
process.roles=broker,controller

# The node id associated with this instance's roles
node.id=1

# The connect string for the controller quorum
controller.quorum.voters=1@localhost:9093


############################# Log Basics #############################

# 日志文件存储位置
log.dirs=/tmp/kraft-combined-logs

4.3 4.x的安装、管理和配置

  • 下载(完全去除zookeeper使用kraft)

kafka 4.1.1推荐jdk版本17

  • windows安装
bash 复制代码
#KRaft模式启动
# 会生成一个唯一的id区别不同的kafka集群
kafka-storage.bat random-uuid
kafka-storage.bat format --standalone -t 上一步生成的uuid -c ..\..\config\server.properties 
#启动kafka 就不用在启动zookeeper
kafka-server-start.bat ..\..\config\server.properties
  • linux安装
bash 复制代码
#KRaft模式启动
# 会生成一个唯一的id区别不同的kafka集群
kafka-storage.sh random-uuid
kafka-storage.sh format --standalone -t 上一步生成的uuid -c ..\..\config\server.properties 
#启动kafka 就不用在启动zookeeper
kafka-server-start.sh ..\..\config/server.properties
  • kafka基本的操作和管理(单机非集群)

和2.x一致命令

  • Broker配置项
properties 复制代码
############################# Server Basics #############################

# The role of this server. Setting this puts us in KRaft mode
process.roles=broker,controller

# The node id associated with this instance's roles
node.id=1

# List of controller endpoints used connect to the controller cluster
controller.quorum.bootstrap.servers=localhost:9093

############################# Log Basics #############################

# A comma separated list of directories under which to store log files
log.dirs=/tmp/kraft-combined-logs

5. kafka clients 版本兼容性

  • Apache kafka 官方有明确的向后兼容性保证
  • 生产者/消费者客户端 可以连接版本相同或更高的Broker
  • 不能保证新版客户端能连接旧版本的Broker

6. Hello kafka

6.1 创建主题

bash 复制代码
# 启动的时候含有下边的意思是可以自动创建主题,也可以根据前边的指令进行创建
 auto.create.topics.enable = true

6.2 生产者发送消息(三种方式)

pom包依赖,我使用的版本3.9.1

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.x</groupId>
    <artifactId>demo_01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo_01</name>
    <description>demo_01</description>
    <properties>
        <java.version>17</java.version>
        <junit-jupiter.version>5.10.2</junit-jupiter.version>
    </properties>
    <dependencies>
        <!-- ✅ 正确的 starter 名称是 spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- ✅ 正确的测试 starter 名称是 spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Kafka 客户端 -->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>3.9.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

代码展示

java 复制代码
  /**
     * TODO 生产者发送消息
     * */
    @Test
    void producer1() {
        // 设置属性
        Properties properties = new Properties();
        // 指定连接的kafka服务器地址
        properties.put("bootstrap.servers","127.0.0.1:9092");
        // 配置多台的服务,用,分割  其中一个宕机,生产者依然可以脸上(集群)
        // 配置string 的序列化(对象 -> 二进制字节数组:能够在网络上传输)
        properties.put("key.serializer",StringSerializer.class);
        properties.put("value.serializer",StringSerializer.class);

        // 构建kafka生产者对象
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
        try {
            ProducerRecord<String,String> record ;
            try {
                // 构建消息
                record = new ProducerRecord<>("iuit", "name", "小明");
                // 发送消息,不知道是否发送成功
                producer.send(record);
                System.out.println("消息已经发送");
            }catch (Exception e){
                e.printStackTrace();
            }
        }finally {
            // 释放连接
            producer.close();
        }
    }
    @Test
    void producer2() {
        // 设置属性
        Properties properties = new Properties();
        // 指定连接的kafka服务器地址
        properties.put("bootstrap.servers","127.0.0.1:9092");
        // 配置多台的服务,用,分割  其中一个宕机,生产者依然可以脸上(集群)
        // 配置string 的序列化(对象 -> 二进制字节数组:能够在网络上传输)
        properties.put("key.serializer",StringSerializer.class);
        properties.put("value.serializer",StringSerializer.class);

        // 构建kafka生产者对象
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
        try {
            ProducerRecord<String,String> record ;
            try {
                // 构建消息
                record = new ProducerRecord<>("iuit", "name", "小王");
                // 发送消息,不知道是否发送成功
                Future<RecordMetadata> send = producer.send(record);
                RecordMetadata recordMetadata = send.get();
                if (null != recordMetadata){
                    System.out.println("offset(偏移量):" + recordMetadata.offset() + ","
                        + "partition(分区):" + recordMetadata.partition()
                    );
                }
                System.out.println("消息已经发送");
            }catch (Exception e){
                e.printStackTrace();
            }
        }finally {
            // 释放连接
            producer.close();
        }
    }
    @Test
    void producer3() {
        // 设置属性
        Properties properties = new Properties();
        // 指定连接的kafka服务器地址
        properties.put("bootstrap.servers","127.0.0.1:9092");
        // 配置多台的服务,用,分割  其中一个宕机,生产者依然可以脸上(集群)
        // 配置string 的序列化(对象 -> 二进制字节数组:能够在网络上传输)
        properties.put("key.serializer",StringSerializer.class);
        properties.put("value.serializer",StringSerializer.class);

        // 构建kafka生产者对象
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
        try {
            ProducerRecord<String,String> record ;
            try {
                // 构建消息
                record = new ProducerRecord<>("iuit", "name", "小王");
                // 发送消息,不知道是否发送成功
                producer.send(record, new Callback() {
                    @Override
                    public void onCompletion(RecordMetadata recordMetadata, Exception e) {
                        if (e == null){
                            System.out.println("没有异常," + "offset:" + recordMetadata.offset() + ","
                                    + "partition:" + recordMetadata.partition());
                        }else{
                            // 异常打印
                            e.printStackTrace();
                        }
                    }
                });
                System.out.println("消息已经发送");
            }catch (Exception e){
                e.printStackTrace();
            }
        }finally {
            // 释放连接
            producer.close();
        }
    }

6.3 顺序保障

kafka消息发送的顺序保障

  • 一个主题一个发呢去。主题就具备顺序性
  • 就算一个主题多个分区,在一个分区内,消息也会具备顺序的

6.4 生产和消费序列化器

java 复制代码
// 配置string 的序列化(对象 -> 二进制字节数组:能够在网络上传输)
properties.put("key.serializer",StringSerializer.class);
properties.put("value.serializer",StringSerializer.class);

kafka的序列化:

Avro:语言无关,序列化后体积小

案例:

  • LinkedIN:Avro
  • 国内大厂:Avro、Protobuf、自定义、json序列化

6.5 分区器

分局上边配置新增

  • 默认分区器会根据key的值进行分区匹配
  • 轮询:将消息平均分配
  • 统一粘性分区器:不管有没有key都会进行分区匹配1
  • 可以直接在发送中指定
  • 自定义分区
java 复制代码
 // 设置属性
        Properties properties = new Properties();
        // 指定连接的kafka服务器地址
        properties.put("bootstrap.servers","127.0.0.1:9092");
        // 配置多台的服务,用,分割  其中一个宕机,生产者依然可以脸上(集群)
        // 配置string 的序列化(对象 -> 二进制字节数组:能够在网络上传输)
        properties.put("key.serializer",StringSerializer.class);
        properties.put("value.serializer",StringSerializer.class);
        // 默认分区
        properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, DefaultPartitioner.class);
        // 轮询分区器
        properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,RoundRobinPartitioner.class);
        // 统一粘性分区器
        properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,UniformStickyPartitioner.class);
        // 构建kafka生产者对象
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
        try {
            ProducerRecord<String,String> record ;
            try {
                // 构建消息
                // record = new ProducerRecord<>("iuit", "name", "小明");
                // 直接指定放在分区1上边
                record = new ProducerRecord<>("iuit", 1,"name", "小明");
                // 发送消息,不知道是否发送成功
                producer.send(record);
                System.out.println("消息已经发送");
            }catch (Exception e){
                e.printStackTrace();
            }
        }finally {
            // 释放连接
            producer.close();
        }
    }
  • 自定义分区实现Partitioner
java 复制代码
public class SelfPartitioner implements Partitioner {
    @Override
    public int partition(String s, Object o, byte[] bytes, Object o1, byte[] bytes1, Cluster cluster) {
        // 根据value值进行分区
        List<PartitionInfo> partitionInfos = cluster.partitionsForTopic(s);
        int num = partitionInfos.size();
        int partId = Utils.toPositive(Utils.murmur2(bytes)) % num;
        return partId;
    }

    @Override
    public void close() {

    }

    @Override
    public void configure(Map<String, ?> map) {

    }
}

6.6 缓冲\批次

7 消费者接受消息的基本流程

7.1 消费这个和消费者组

核心规则:

  1. 一个分区只能被同一消费者组内的一个消费者独占消费
  2. 消费者数量 <= 分区数:每个消费者分配一个或者多个分区
  3. 消费者数量> 分区数:多个消费者处于空闲状态
  4. 不同消费者组可独立消费同一主题,互不影响


7.2 消费者配置

  • partition.assignment.strategy

分配分区策略

  • range:按照主题逐个分配,将连续的分区范围分配和消费者,可能导致负载不均衡,过载的问题

  • RoundRobinAssignor:跨所有主题轮询分配,实现全局负载均衡,但是在消费订阅不同的主题会出现严重的负载不均衡的问题


  • StickyAssignor机制:
    • 首次分配:尽可能负载均衡
    • 重平衡时:尽量保证原有分配,减少分区迁移
    • 解决上面Range,RoundRobin在重平衡时的"颠簸"问题

  • CooperativeStickyAssignor:增强版,粘性分析器
    • kafka2.4后引入,Sticky的增强版本

  • max.partition.fetch.bytes

指定服务器给每消费者每个分区的最大字节数

  • auto.offset.reset

首次消费的时候决定在哪里偏移量进行消费

消费者消费kafka的时候确认是否存活

  • enable.auto.commit

默认为true,自动提交,默认程序运行的时候

消费者名字

  • max.poll.records

控制每次拉取的最大消息,默认是500

  • fetch.min.bytes

配合fetch.wait.max.bytes,协同等待策略,等待最少多少字节消费

  • receive.buffer.bytes

tcp发送缓冲区大小

配合fetch.min.bytes,协同等待策略

  • send.buffer.bytes

tcp接收缓冲区大小

  • 代码演示
java 复制代码
/**
     * TODO 消费者 为什么之前的没有消费的消息没有消费?
     * */

    @Test
    void consumer01() {
        // 设置属性
        Properties properties = new Properties();
        // 指定kafka服务器地址
        properties.put("bootstrap.servers","127.0.0.1:9092");
        // 设置string反序列化
        properties.put("key.deserializer", StringDeserializer.class);
        properties.put("value.deserializer",StringDeserializer.class);
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"groupB");
        // RangeAssignor(默认策略):按主题逐个分配,将连续的分区范围分配给消费者
//        properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.RangeAssignor");
        // RoundRobinAssignor:跨所有主题轮询,实现全局负载均衡
//        properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.RoundRobinAssignor");
        // StickyAssignor(推荐):初始分配均衡,重平衡时保留原有分配,仅调整必要分区
        properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.StickyAssignor");
        // CooperativeStickyAssignor 上一个增强版 粘性分配器
//        properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.CooperativeStickyAssignor");
        // 默认值latest:消息启动后接收到的消息;earliest:从头开始消费所有的
        properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"latest");
        /* 取消自动提交*/
        properties.put("enable.auto.commit",false);
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
        try {
            consumer.subscribe(Collections.singletonList("iuit"));
            // 使用消费者拉去消息
            while (true){
                // 每隔1秒拉取一次消息
                ConsumerRecords<String, String> poll = consumer.poll(Duration.ofSeconds(1));
                for (ConsumerRecord<String, String> record : poll) {
                    String key = record.key();
                    String value = record.value();
                    System.out.println("接收到的消息,key为:" + key + ";value为:" + value);
                }
                // 取消自动提交
                consumer.commitAsync();// 异步提交:不阻塞我们的应用程序的线程,不会重试(有可能失败)
            }
        }finally {
            try {
                consumer.commitSync();// 同步提交:会阻塞我们的应用线程,会重试(一定会成功)
            }finally {
                // 释放连接
                consumer.close();
            }

        }
    }

8. kafka的SpringBoot实战

  • maven依赖
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.x</groupId>
    <artifactId>demo_01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo_01</name>
    <description>demo_01</description>
    <properties>
        <java.version>17</java.version>
        <junit-jupiter.version>5.10.2</junit-jupiter.version>
    </properties>
    <dependencies>
        <!-- ✅ 正确的 starter 名称是 spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- ✅ 正确的测试 starter 名称是 spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Kafka 客户端 -->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>3.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>3.3.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka-test</artifactId>
            <version>3.3.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  • 配置文件
yaml 复制代码
spring:
  application:
    name: demo_01
  kafka:
    bootstrap-servers: localhost:9092 # kafka服务器地址
    # 生产者配置
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      # 消息确认机制:0-不确认 1-leader确认 all-所有副本确认
      acks: all
      # 发送失败重试次数
      retries: 3
      # 批量发送大小
      batch-size: 16384
      # 发送缓冲区大小
      buffer-memory: 33554432
    # 消费者配置
    consumer:
      group-id: iuit-group
      key-deserializer: org.apache.kafka.common.serialization.StringSerializer
      value-deserializer: org.apache.kafka.common.serialization.StringSerializer
      # 自动提交offset 偏移量
      enable-auto-commit: true
      auto-commit-interval: 1000
      # 自动配置offset:earliest-从最早开始 latest-从最新开始
      auto-offset-reset: earliest
    # 监听器配置
    listener:
      # 批量消费模式
      type: single # batch
      # 手动提交ack
      ack-mode: manual_immediate
  • 配置文件

KafkaConsumerConfig

java 复制代码
@Configuration
public class KafkaConsumerConfig {
    @Bean
    public ConsumerFactory<String,String> consumerFactory(){
        HashMap<String, Object> map = new HashMap<>();
        map.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"localhost:9092");
        map.put(ConsumerConfig.GROUP_ID_CONFIG,"iuit1-group");
        map.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        map.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        // 可选 自定义配置
        map.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
        map.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);
        map.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,10);
        return new DefaultKafkaConsumerFactory<>(map);

    }
    @Bean("batchFactory")
    public ConcurrentKafkaListenerContainerFactory<String,String> consumerBatchFactory(){
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        factory.setBatchListener(true);
        factory.getContainerProperties().setPollTimeout(3000);
        // 配置手动提交
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
        return factory;
    }
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String,String> kafkaListenerContainerFactory(){
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        // 配置手动提交
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
        // 设置并发消费者数量
        factory.setConcurrency(3);
        return factory;
    }
}

KafkaProducerConfig

java 复制代码
@Configuration
public class KafkaProducerConfig {
    @Bean
    public ProducerFactory<String,String> producerFactory(){
        HashMap<String, Object> map = new HashMap<>();
        map.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"localhost:9092");
        map.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        map.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class);
        // 可选 自定义配置
        map.put(ProducerConfig.ACKS_CONFIG,"all");
        map.put(ProducerConfig.RETRIES_CONFIG,3);
        map.put(ProducerConfig.BATCH_SIZE_CONFIG,16384);
        map.put(ProducerConfig.LINGER_MS_CONFIG,1);
        map.put(ProducerConfig.BUFFER_MEMORY_CONFIG,3354432);
        return new DefaultKafkaProducerFactory<>(map);
    }
    @Bean
    public KafkaTemplate<String,String> kafkaTemplate(){
        return new KafkaTemplate<>(producerFactory());
    }
}
  • 生产者
java 复制代码
@Service
public class KafkaProducerService {
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;
    // 1.同步发送
    public void sendMessageSync(String topic,String message){
        try {
            SendResult<String, String> result = kafkaTemplate.send(topic, message).get();
            System.out.println("发送成功:" + result.getRecordMetadata().topic() +
                    ";partition:" + result.getRecordMetadata().partition() +
                    ";offset:" + result.getRecordMetadata().offset()
            );
        }catch (Exception e){
            System.out.println("发送失败:" + e.getMessage());
        }
    }

    // 2.异步发送
    public void senfMessageAsync(String topic,String message){
        CompletableFuture<SendResult<String, String>> send = kafkaTemplate.send(topic, message);
        send.whenComplete((result,ex)->{
           if (ex == null){
               System.out.println("异步发送成功:" + result.getRecordMetadata().toString());
           }else {
               System.err.println("异步发送失败:" + ex.getMessage());
           }
        });
    }
    // 3.带key的发送
    public void senfMessageWithKey(String topic,String key,String value){
        kafkaTemplate.send(topic,key,value);
    }
    // 发送指定分区
    public void sendToPartition(String topic,Integer partition, String key,String value){
        kafkaTemplate.send(topic,partition,key,value);
    }
}
  • 消费者
java 复制代码
@Component
public class KafkaConsumerService {
    private static final Logger logger = LoggerFactory.getLogger(KafkaConsumerService.class);
    /**
     * TODO 基础消费
     * */
    @KafkaListener(topics = "iuit-topic",groupId = "iuit-group")
    public void consumerMessage(String message){
        logger.info("接受到消息:{}",message);
    }
    /**
     * TODO 获取完整消息的消息
     * */
    @KafkaListener(topics = "iuit-topic",groupId = "iuit1-group")
    public void consumerFullMessage(ConsumerRecord<String,String> record){
        logger.info("接收到的完整消息:topic={},partition={},offset={},key={},value={}",
                record.topic(),
                record.partition(),
                record.offset(),
                record.key(),
                record.value()
                );

    }
    /**
     * TODO 批量消费
     * */
    @KafkaListener(topics = "batch-topic",groupId = "batch-group",containerFactory = "batchFactory")
    public void consumerBatchMessages(List<ConsumerRecord<String,String>> records){
        logger.info("批量接收到{}条消息",records.size());
        for (ConsumerRecord<String, String> record : records) {
            logger.info("批量消息:{}",record.value());
        }
    }
    /**
     * TODO 监听多个topic
     * */
    @KafkaListener(topics = {"topic1","topic2","topic3"},groupId = "multi-group")
    public void  consumerMultipleTopics(String message){
        logger.info("收到多个主题消息:{}",message);
    }

    /**
     * TODO 收到提交offest
     * */
    @KafkaListener(topics = "manual-topic",groupId = "manual-group")
    public void consumerWithManualCommit(ConsumerRecord<String,String> record, Acknowledgment acknowledgment){
        try {
            logger.info("处理消息:{}",record.value());
            System.out.println("处理业务。。。。");
            // 手动提交offset
            acknowledgment.acknowledge();
        }catch (Exception e){
            logger.error("处理消息失败:" + e);
        }
    }



}
  • api 接口调用
java 复制代码
@RestController
@RequestMapping("/kafka")
public class KafkaController {
    @Autowired
    private KafkaProducerService kafkaProducerService;
    @PostMapping("/send")
    public String senfMessage(@RequestParam String topic,@RequestParam String message){
        kafkaProducerService.senfMessageAsync(topic,message);
        return "消息发送成功";
    }
    @PostMapping("/sendAsync")
    public String sendMessageAsync(@RequestParam String topic,
                                   @RequestParam String meaasge){
        kafkaProducerService.senfMessageAsync(topic,meaasge);
        return "异步消息发送中";
    }
}

9 kafka消息发送和消费的过程

10. kafka集群原理

  • Controller选举和Broker注册

  • kafka的副本机制

    • Leader副本(首领副本)
    • Follower副本(跟随者副本)
  • 集群中的核心概念和参数

  • 复制系数:主题级别 default.replication.factor (默值3)
  • 不完全首领选举:分区首领不可用切没有同步副本 unclean.leader.election.enable
  • 最少同步副本
  • 副本分布策略

11. kafka的零拷贝

四次交换

kafka的transferTo

linux系统的改进

kafka的零拷贝运用

12. 流式处理

官网

  • 流式处理命令
bash 复制代码
# 创建输入Topic'streams-plaintext-input' 和输出Topic 'streams-wordcount-output'
./kafka-topics.sh --bootstrap-server localhost:9092 --create --topic streams-plaintext-input --partitions 1 --replication-factor 1

./kafka-topics.sh --bootstrap-server localhost:9092 --create --topic streams-wordcount --partitions 1 --replication-factor 1

# 启动WordCount应用
./kafka-run-class.sh org.apache.kafka.streams.examples.wordcount.WordCountDemo

# 启动生产者和消费者
./kafka-console-producer.sh --bootstrap-server localhost:9092 --topic streams-plaintext-input
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic streams-plaintext-input
--form-beginning --property print.ket=true --property print.value=true --property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer --property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer
相关推荐
阴暗扭曲实习生2 小时前
135编辑器开放平台架构解析:企业级富文本接入方案的技术实现
java·开发语言·中间件
Francek Chen3 小时前
【大数据存储与管理】分布式数据库HBase:03 HBase数据模型
大数据·数据库·hadoop·分布式·hdfs·hbase
伟大的大威10 小时前
NVIDIA DGX Spark (Blackwell GB10) 双机 196B Step 3.5 Flash 大模型部署完整实录
分布式·spark·nvidia
江不清丶14 小时前
Kafka消息积压排查与治理:从应急处理到长期优化
数据库·kafka·linq
回家路上绕了弯16 小时前
Claude Code Agent Team 全解析:AI 集群协作,重构代码开发新范式
人工智能·分布式·后端
初次攀爬者17 小时前
Redis与数据库的数据一致性方案解析
数据库·redis·分布式
切糕师学AI18 小时前
Kubernetes Operator 详解
运维·分布式·云原生·容器·kubernetes·自动化·运维自动化
梵得儿SHI18 小时前
Spring Cloud 高并发订单服务实战:从创建流程优化到 Seata 分布式事务落地(附代码 + 架构图)
分布式·spring·spring cloud·高并发·异步削峰·完整解决方案·限流降级
阿坤带你走近大数据21 小时前
大数据处理与分布式存储的各自介绍
分布式·云原生·实时数仓·存储·数据处理·数据湖仓