Kafka快速入门

目录

1、 消息中间件概述

1.1 消息队列简介

1.2 消息队列应用场景

1.2.1 系统解耦

1.2.2 流量消锋

1.2.3 异步通信

1.3 MQ的优缺点

1.4 Kafka简介

1.5 MQ产品比对

2、 Kafka环境搭建

2.1 Kafka集群机制

2.2 Kafka基础架构

2.3 Kafka集群搭建

2.4 Kafka集群测试

3、Kafaka的 Java API入门案例

3.1 发送消息

3.2 消费消息

4、 在Spring Boot中集成Kafka

4.1 生产者

4.1.1 环境搭建

4.1.2 发送消息

4.1.3 发送方式

1、不关心发送结果

2、同步消息发送

3、异步消息发送

4.1.4 拦截器配置

4.1.6 分区

4.1.7 生产者常见属性

batch.size

linger.ms

buffer.memory

compression.type

acks属性

retries属性

4.2 消费者

4.2.1 环境搭建

4.2.2 消费消息

4.2.3 消费者的手动位移提交

4.2.4 消费时的异常处理

5.Kafka的高级内容

补充:docker-compose文件说明

文件版本

服务定义

Zookeeper 服务

Kafka 实例

Kafka Eagle 服务

数据卷的定义


1、 消息中间件概述

1.1 消息队列简介

消息队列(message queue)简称MQ,是一种以“先进先出”的数据结构为基础的消息服务器。

消息:两个系统间要传输的数据

作用:实现消息的传递

原始的数据传递方式:

上述的数据传输方式为同步传输【作为调用方必须等待被调用方执行完毕以后,才可以继续传递消息】,同步传输存在的弊端:传输效率较低

基于MQ实现消息的传输,如下图所示:

上述的数据的传输方式属于异步传输【作为调用方法不用等待被调用方执行完毕就可以接续传递消息】,数据传输的效率较高。

1.2 消息队列应用场景

首先我们先说一下消息中间件的主要的作用:

[1]系统解耦

[2]流量消锋

[3]数据分发

上面的三点是我们使用消息中间件最主要的目的。

1.2.1 系统解耦

系统的耦合性越高,容错性【是指系统在部分组件(一个或多个)发生故障时仍能正常运作的能力】就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。

如下下图所示:

使用消息队列以后,整个下单操作的架构如下图所示:

使用消息队列解耦合,系统的耦合性就会降低了,容错性就提高了。比如物流系统发生故障,需要几分钟才能来修复,在这段时间内,物流系统要处理的数据被缓存到消息队列中,用户的下单操作正常完成。当物流系统恢复后,补充处理存在消息队列中的订单消息即可,终端系统感知不到物流系统发生过几分钟故障。

1.2.2 流量消锋

流量消锋:消除系统中的高峰值流量(流量可以理解为就是请求)

现有一个电商系统下单初始架构如下所示:

假设用户每秒需要发送5k个请求,而我们的A系统每秒只能处理2K个请求,这样就会导致大量的下单请求失败。而且由于实际请求的数量远远超过系统的处理能力,此时也有可能导致系统宕机。

使用消息队列改进以后的架构如下所示:

用户每秒发送5k个请求,我们可以先将下单请求数据存储到MQ中,此时在MQ中就缓存了很多的下单请求数据,而A系统根据自己的处理能力从MQ中获取数据进行下单操作,有了MQ的缓存层以后,就可以保证每一个用户的下单请求可以得到正常的处理,并且这样可以大大提高系统的稳定性和用户体验。

1.2.3 异步通信

假设A系统进行了某一个业务操作以后,需要将这个业务操作结果通知给其他的系统,原始的架构如下所示:

此时B系统、C系统、D系统就需要提供对应的接口,然后让A系统进行调用。如果此时不需要通知D系统了,那么就需要更改A系统的代码,将调用D系统的代码删除掉。并且如此时项目中添加了一个新的系统E,A系统也需要将处理结果通知给E系统,那么同时也需要更改A系统的代码。这样就不利于后期的维护。

使用MQ改进以后的架构如下所示:

A系统需要将业务操作结果通知给其他的系统时,A系统只需要将结果发送到MQ中。其他的系统只需要从MQ中获取结果即可,如果不需要结果了,此时只需要取消从MQ中获取结果的操作即可。并且如果新增了一个系统需要获取结果,只需要从MQ中获取结果数据就可以了,A系统的代码不需要进行改动。这样就大大的提高了系统的维护性。

1.3 MQ的优缺点

优点:

1、应用解耦提高了系统的容错性

2、流量消锋提高了系统的并发能力

3、异步通信提高了系统的可维护性

缺点:

1、系统可用性降低:系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。

2、系统复杂度提高:MQ的加入大大增加了系统的复杂度。

MQ的选择依据是什么? 调用方是否需要获取到被调用方的执行结果,如果需要获取到结果,那么就需要使用同步通信,如果不需要就可以使用异步通信。

1.4 Kafka简介

Kafka是Apache开源的一款基于zookeeper协调的分布式消息系统,具有高吞吐率、高性能、实时、高可靠等特点,可实时处理流式数据。它最初由LinkedIn公司开发,使用Scala语言编写。

Kafka历经数年的发展,从最初纯粹的消息引擎,到近几年开始在流处理平台生态圈发力,多个组织或公司发布了各种不同特性的产品。

常见产品如下:

1、Apache Kafka :最“正统”的Kafka也是开源版,它是后面其他所有发行版的基础

2、Cloudera/Hortonworks Kafka: 集成了目前主流的大数据框架,能够帮助用户实现从分布式存储、集群调度、流处理到机器学习、实时数据库等全方位的数据理。

3、Confluent Kafka :主要提供基于Kafka的企业级流处理解决方案。

Apache Kafka,它现在依然是开发人数最多、版本迭代速度最快的Kafka,我们使用此产品学习。我们使用版本kafka_2.13-2.8.1

官网地址:Apache Kafka

下载:Apache Kafka

1.5 MQ产品比对

市面上常见的消息队列产品:

1、ActiveMQ

2、RabbitMQ

3、RocketMQ

4、Kafka

常见特性比对:

特性 ActiveMQ RabbitMQRocketMQKafka
开发语言javaerlangjavascala
单机吞吐量万级万级10万级100万级
时效性msusmsms级以内
可用性高(主从)高(主从)非常高(分布式)非常高(分布式)
功能特性成熟的产品、较全的文档、各种协议支持好并发能力强、性能好、延迟低,社区活跃度高,数据量没有那么大,优先选择功能比较完备的RabbitMQMQ功能比较完善,扩展性佳,可靠性要求高的金融互联网领域使用多,稳定性高,经历了多次阿里双11考验只支持主要的MQ功能,大数据领域使用多,追求高吞吐量,适合产生大量数据的互联网服务的数据收集业务

2、 Kafka环境搭建

准备linux基础环境:只用安装docker+关闭了防火墙

1.将需要的镜像文件放到Linux中的/opt目录下:

2.然后把tar文件恢复成为镜像

docker load -i zookeeper.tar
docker load -i kafka.tar
docker load -i efak.tar

docker images   

3.把docker-compose-kafka.yml也上传到Linux中的/opt目录下:

  • 主要修改这个文件里面的地址,详情请看最末尾的补充章节,再修改

4.在 docker-compose文件所在的目录中运行以下命令:

docker compose -f docker-compose-kafka.yml up -d

#重启
docker compose -f docker-compose-kafka.yml restart

5.验证服务是否运行

docker ps

6.访问efak主页:虚拟机ip:8084:

  • 账号:admin;密码:123456

2.1 Kafka集群机制

集群机制说明:

1、Kafka是天然支持集群的,哪怕是一个节点实际上也是集群模式

2、Kafka集群依赖于zookeeper进行协调,并且在早期的Kafka版本中很多数据都是存放在Zookeeper中的

3、Kafka节点只要注册到同一个Zookeeper上就代表它们是同一个集群的

4、Kafka通过brokerId(kafka节点的id)来区分集群中的不同节点

2.2 Kafka基础架构

Kafka的核心角色介绍:

角色名称 具体含义
BrokerBroker是一个kafka实例,简单说就是一台kafka服务器,kafkaCluster表示集群。
Topic主题 ,Kafka将消息进行分类,每一类的消息称之为一个主题。
Producer生产者,向Broker topic 发布消息的客户端。
Consumer消费者,从Broker topic 订阅消息的客户端。
PartitionTopic的分区,每个 Topic 可以有多个分区,同一个Topic中,不同分区的数据是不重复的。每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。分区作用是做负载,提高 kafka 的吞吐量。
ReplicationPartition(分区)的副本。每个分区可以有多个Replication,由一个Leader和若干个Follower组成。Leader负责接收生产者push的消息和消费者poll消费消息。Follower会实时从自己的Leader中同步数据保持同步。Leader故障时,某个Follower会上位为新的Leader。分区副本的作用是保证高可用。
ConsumerGroup同一个消费者组中的多个消费者分摊一个topic中的消息,不同消费者组中的多个消费者共同消费一个topic中的相同消息
In-sync Replicas(ISR)(ISR)已同步副本:表示存活且副本都已和Leader同步的的broker集合,是Leader所有replicas副本的子集。如果某个副本节点宕机,该副本就会从ISR集合中剔除。
ZooKeeperKafka使用ZooKeeper来进行集群管理、协调和元数据存储。Kafka中的Broker、Topic、Consumer都会注册到zookeeper。

2.3 Kafka集群搭建

为了测试方便,我们选择搭建伪分布式Kafka集群,在同一台虚拟机上启动一个zookeeper实例三个Kafka实例。并且使用docker compose进行搭建,具体的docker compose文件的内容参考课程资料: docker-compose-kafka.yml

访问EFAK账号和密码: admin/123456

这部分其实就是第2章最开始的环境搭建

2.4 Kafka集群测试

进入任意一个容器:

docker exec -it kafka1 /bin/bash

topic操作相关命令

# 创建主题
kafka-topics.sh --create --topic hellokafka-topic --bootstrap-server 192.168.80.129:9092
# 创建主题,在这个kafka容器中也可以创建其他kafka集群的主题
kafka-topics.sh --create --topic hellokafka-topic1 --bootstrap-server 192.168.80.129:9093

# 创建3个分区,3个副本的kafka
kafka-topics.sh --create --topic myclustertopic --partitions 3 --replication-factor 3 --bootstrap-server 192.168.80.129:9092

# 查看系统中所有kafka集群的topic,可以尝试从其他的节点上进操作
kafka-topics.sh --list --bootstrap-server 192.168.80.129:9092

# 查看某个topic的详情信息
kafka-topics.sh --describe --topic hellokafka-topic --bootstrap-server 192.168.80.129:9092     

上述查询信息的说明:

Topic: 主题名称

TopicId: 主题id

PartitionCount:分区个数

ReplicationFactor:复制副本数

Configs: 主题配置信息

Partition:分区的id(分区编号)

Leader:主分区所在的Kafka服务器的id,第0号分区在第2台kafka主机上

Replicas:副本分区所在的kafka服务器的id

Isr:已同步副本所在的节点id

发送以及接收消息:

# 使用生产者脚本发送消息
kafka-console-producer.sh  --topic hellokafka-topic --bootstrap-server 192.168.100.102:9092

# 使用生产者脚本接收消息
# 从当前位置开始消费
kafka-console-consumer.sh --topic hellokafka-topic --bootstrap-server 192.168.100.102:9092

# --from-beginning: 表示从最开始的位置进行消费消费
kafka-console-consumer.sh --topic hellokafka-topic --from-beginning --bootstrap-server 192.168.100.102:9092

注意:EFAK删除主题的时候获取admin token需要进入到容器中,查看system-config.properties配置文件进行获取。

docker exec -it eagle /bin/bash
cat conf/system-config.properties


得到如下配置:可知token是keadmin
######################################
# delete kafka topic token
# Set to delete the topic token, so that administrators can have the right to delete
######################################
efak.topic.token=keadmin

3、Kafaka的 Java API入门案例

3.1 发送消息

具体步骤如下所示:

1、创建一个kafka-parent父工程,删除src目录,并加入如下依赖:

<!-- 父工程 , JDK选择17 -->
<parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>3.0.5</version>
</parent>

<!--kafka-clients 2023.8:Kafka的Java API依赖-->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>3.5.1</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

2、在kafka-parent父工程下创建kafka-producer子工程

3、创建启动类

package com.atguigu.kafka;

@SpringBootApplication
public class ProducerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class , args) ;
    }

}

5、生产者代码实现:

官网示例代码:KafkaProducer (kafka 3.5.2 API)

测试代码:

// 可以事先不用创建主题,会自动创建
package com.atguigu.kafka;

@SpringBootTest(classes = ProducerApplication.class) //classes属性;加载启动类的环境
public class ProducerDemo01 {

    // 定义主题的名称
    public static final String TOPIC_NAME = "hellokafka";

    @Test
    public void sendMsg() {

        //创建Properties对象,配置 Kafka 生产者的各种参数
        Properties properties = new Properties() ;
        //配置Kafka生产者要连接的 Kafka 集群的地址和端口
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG , "192.168.100.102:9092") ;
        //指定了用于序列化消息键的类。在这个例子中,使用的是 StringSerializer,这意味着消息的键将被序列化为字符串格式
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG , "org.apache.kafka.common.serialization.StringSerializer") ;
        //指定了用于序列化消息值的类。与键相同,这里也使用 StringSerializer,表示消息的值同样将被序列化为字符串格式。
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG , "org.apache.kafka.common.serialization.StringSerializer") ;

        // 传入Properties对象 根据配置 创建Kafka的生产者对象KafkaProducer
        KafkaProducer kafkaProducer = new KafkaProducer(properties) ;

        // 调用send方法发送消息
        for (int i = 0; i < 10; i++) {
            
            //创建生产者消息对象,指定向哪个主题发送什么消息
            ProducerRecord producerRecord = new ProducerRecord(TOPIC_NAME , "helloKafka~" + i) ;
            // 调用KafkaProducer对象的send方法发送消息
            kafkaProducer.send(producerRecord) ;
        }

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

    }

}

消息发送完毕以后可以通过eagle系统查看主题消息。

3.2 消费消息

具体步骤如下所示:

1、在kafka-parent父工程下创建kafka-consumer子工程

2、创建启动类

package com.atguigu.kafka;

@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class , args) ;
    }

}

3、消费者代码实现:

官网示例代码:KafkaConsumer (kafka 3.5.2 API)

测试代码:

package com.atguigu.kafka;

@SpringBootTest(classes = ConsumerApplication.class) //classes属性;加载启动类的环境
public class ConsumerDemo01 {

    // 定义主题的名字
    public static final String TOPIC_NAME = "hellokafka";

    @Test
    public void consumerMsg() {

        // 创建属性对象,配置 Kafka 消费者的各种参数
        Properties properties = new Properties() ;
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG , "192.168.100.102:9092") ;
        properties.put(ConsumerConfig.GROUP_ID_CONFIG , "group01") ; //指定消费者组id
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG , "org.apache.kafka.common.serialization.StringDeserializer") ;
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG , "org.apache.kafka.common.serialization.StringDeserializer") ;
        // earliest:最开始第一次向一个topic发消息时,如果没有消费位移,那么此时从最早(最小)偏移量开始读取消息
        // latest:最开始第一次向一个topic发消息时,如果没有消费位移,那么此时从最新(最大)偏移量开始读取消息
        // 查看消费位移的命令如下:
        // kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group group01 --describe
        //生产者已经发了些消息了,然后消费者才开始监听,加这个属性后,就会从消息队列的头部开始消费;写laters则只会监听现在发来的消息,以前发来的不监听
        properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG , "earliest") ;   

        //入Properties对象 根据配置 创建Kafka的消费者对象KafkaConsumer
        KafkaConsumer kafkaConsumer = new KafkaConsumer(properties) ;

        // 调用KafkaConsumer对象的subscribe方法,让消费者订阅某些主题
        kafkaConsumer.subscribe(Arrays.asList(TOPIC_NAME));

        while (true) {
            //调用kafkaConsumer对象的poll方法从主题中拉取一批消息,kafka发生消息是一批一批的发
            ConsumerRecords<String , String> consumerRecords = kafkaConsumer.poll(Duration.ofMillis(100));  // 参数表示拉取消息的时间间隔
            // 消费消息
            for (ConsumerRecord<String , String> record : consumerRecords) {
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());  // offset表示消费位移
            }

        }

    }

}

4、 在Spring Boot中集成Kafka

4.1 生产者

4.1.1 环境搭建

具体步骤如下所示:

1、创建一个spring-kafka父工程,并添加如下依赖

<parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>3.0.5</version>
</parent>

<dependencies>
    
    <!-- web开发起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- lombok依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- spring boot和junit整合时候的起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!--spring-kafka-->
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    
    <!--hutool-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.19</version>
    </dependency>
    
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

2、在spring-kafka父工程下创建spring-kafka-producer子工程

3、创建启动类

package com.atguigu.kafka;

@SpringBootApplication
public class ProducerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class , args) ;
    }

}

4、在spring-kafka-producer子工程中添加application.yml配置文件,并配置生产者如下:

# application.yml
server:
  port: 8081

# 生产者配置
spring:
  kafka:
    bootstrap-servers: 192.168.100.102:9092,192.168.100.102:9093,192.168.100.102:9094  #配置集群中的所有节点
    producer:
      acks: -1
      retries: 0
      batch-size: 16384 # 批次大小 单位byte,攒到多少byte就把消息批量发送出去
      buffer-memory: 33554432
      compression-type: gzip
      key-serializer: org.apache.kafka.common.serialization.StringSerializer # key的序列化器
      value-serializer: org.apache.kafka.common.serialization.StringSerializer # value的序列化器
      properties: {
        'linger.ms': 10   #最多10ms就会把消息批量发出去
      }   # 配置其他的属性

5、创建主题的配置类

package com.atguigu.kafka.config;

@Configuration
public class KafkaConfig {

    @Bean
    public NewTopic springTestTopic(){
        return TopicBuilder.name("topic-01") //创建主题:启动程序后,这个主题就会自动创建在kafka的服务器中
                .partitions(3) //主题的分区数量
                .replicas(3) //主题的副本数量
                .build();
    }
}

6、启动 主启动类

7、启动程序通过eagle查看主题创建情况

4.1.2 发送消息

创建测试类通过KafkaTemplate发送消息,代码如下所示:

package com.atguigu.kafka.test
    
@SpringBootTest(classes = ProducerApplication.class)
public class ProducerApplicationTest {

    @Autowired
    private KafkaTemplate kafkaTemplate ;

    @Test
    public void send() {
        //向哪个主题发送什么消息
        kafkaTemplate.send("topic-01" , "kafka...producer...send...message...") ;
    }

}

执行测试代码,通过Eagle控制台可以查看到消息发送成功。

上图中的说明:

  • Partition:代表分区的编号,这个topic中有三个分区,分别是0,1,2三个分区
  • LogSize:代表此分区中的消息数量
  • Leader:代表某个分区在哪台kafka服务器上。以第一行为例,表示2号分区在编号为2的kafka服务器上
  • Replicas:某个分区的副本在哪些kafka服务器上(主分区也是算成一个副本)

offset偏移量说明,如下图所示查询topic中某个分区里的消息:

  • offset偏移量是什么?
    • offset 是 partition 中每条消息的唯一标识,从0开始,每当有新的消息写入分区时,offset 就会加 1。offset 是不可变的,即使消息被删除或过期,offset 也不会改变或重用。
    • 消费者在消费 Kafka 消息时,维护了一个当前消费的 offset 值,以及一个已提交的 offset 值
      • 当前消费的 offset 值:表示消费者消费消息的进度;
      • 已提交的 offset 值:表示消费者确认消费过的消息的位置。
    • 消费者在消费完一条消息后,需要提交 offset 来更新已提交的 offset 值,否则会导致消息重复消费。提交 offset 的方式有两种:自动提交和手动提交。
    • 更多offset资料可以参考这篇文章:kafka offset

4.1.3 发送方式

生产者发送消息存在三种方式:

1、不关心发送结果

把消息发送给服务器,但不关心它是否正常到达。这种发送消息的方式是kafka吞吐量最高的一种方式,生产者发送消息后,不需要等待服务器的响应。但是,此种发送消息的方式也是最不可靠的一种方式,因为对于发送失败的消息没有做任何处理

// 入门案例的消息发送方式就是该方式
kafkaTemplate.send("topic-01" , "kafka...producer...send...message...") ;

特点:性能最好,可靠性最差。

2、同步消息发送

使用send()方法发送消息,它会返回一个CompletableFuture对象,再调用其get方法,get方法会一直阻塞到该线程的任务得到返回值,也就是broker返回发送成功。如果业务上关心发送结果,那么可以使用同步发送的方式。

// 实现通过消息发送就需要在调用完send方法以后,再次调用get方法
kafkaTemplate.send("topic-01" , "kafka...producer...send...message...").get() ;

特点:性能最差,可靠性较好。

3、异步消息发送

调用send()方法,并指定一个回调函数,服务器在返回响应时调用该回调函数

如果业务上关心发送结果,且需要异步发送,那么可以用异步+回调的方式来发送消息。

注意:由于是异步发送消息,测试的时候可以让线程休眠一会儿以等待回调函数的执行

// 要实现异步消息发送就需要定义生产者监听器,在发送完毕以后就会根据具体的发送结果调用对应的函数,如下所示:
package com.atguigu.kafka.listener;

@Component
@Slf4j
public class KafkaSendResultHandler implements ProducerListener {

    /**
     * Kafka发送成功回调
     * @param producerRecord
     * @param recordMetadata
     */
    @Override
    public void onSuccess(ProducerRecord producerRecord, RecordMetadata recordMetadata) {
        String topic = producerRecord.topic();
        String value = producerRecord.value().toString();
        Integer partition = recordMetadata.partition();
        log.info("topic:{},value:{},partition:{}, 发送成功回调",topic,value, partition);
    }

    @Override
    public void onError(ProducerRecord producerRecord, RecordMetadata recordMetadata, Exception exception) {
        String topic = producerRecord.topic();
        String value = producerRecord.value().toString();
        log.info("topic:{},value:{}, 发送失败,原因:{}",topic,value,exception.getMessage());
    }
}

4.1.4 拦截器配置

4.1.6 分区

4.1.7 生产者常见属性

生产者还有很多可配置的参数,在 Kafka文档里都有说明,它们大部分都有合理的默认 值,所以没有必要去修改它们 。不过有几个参数在内存使用、性能和可靠性方面对生产者影响比较大,接下来我们会一一说明。

官网地址:Apache Kafka

batch.size

作用:攒一批消息,然后到达指定的内存大小后就会一起发送出去。按照字节数计算,默认值为16384byte(16K)

linger.ms

作用:该参数指定了生产者在发送批次之前等待更多消息加入批次的时间 。KafkaProduce会在批次填满或linger.ms达到上限时把批次发送出去。

默认值为0:意思就是消息必须立即被发送,但这样会影响性能,一般设置10毫秒左右,就是说这个消息发送完后会进入本地的一个batch,如果10毫秒内,这个batch满了16kb就会随batch一起被发送出去。如果10毫秒内,batch没满,那么也必须把消息发送出去,不能让消息的发送延迟时间太长!

buffer.memory

设置发送消息的本地缓冲区,消息会先发送到本地缓冲区,可以提高消息发送性能,默认值是33554432,即32MB

compression.type

:将消息采用特定的压缩算法进行压缩并存储,待消费时再解压。提高了消息的传输效率并且降低了存储压力。

Kafka中提供了四种压缩算法,对比如下所示:CPU资源充足,带宽资源有限时可以考虑使用压缩算法压缩消息。

压缩类型压缩比率CPU 使用率压缩速度带宽使用率
gzip
snappy一般一般一般一般
lz4
zstd一般一般一般一般
acks属性

acks保证生产者将消息可靠的发送到达broker。

常见取值说明:

1、acks=0:生产者发送消息后,就不管了,可靠性差数据会丢,效率最高

2、acks=1:生产者发送消息后,只需要Leader确认即可返回,可靠性中等,效率中等

3、acks=-1(all):生产者发送消息后,需要Leader和ISR队列里面所有Follwer应答,只要其中有一个没有应答就返回消息发送失败,可靠性高效率最低

在生产环境中选择:

1、acks=0,很少使用

2、acks=1,一般用于传输普通日志,允许丢个别数据;

3、aks=-1(all),一般用于传输重要不能丢失的数据(例如:钱、订单、积分等),对可靠性要求比较高的场景。

retries属性

kafka是一种分布式消息系统,常用于大规模数据的收集和分发。在生产者发送消息到kafka集群的过程中,由于多种原因(网络故障、消息格式问题等),可能会发生消息发送失败。为了提高消息传输的可靠性,kafka提供了一种重试机制,即当消息发送失败时,会自动尝试重新发送直到消息成功被写入kafka。作用:消息发送失败后,指定重新发送几次

4.2 消费者

4.2.1 环境搭建

具体步骤如下所示:

1、在spring-kafka父工程下创建spring-kafka-consumer子工程

2、创建启动类

package com.atguigu.kafka;
@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class , args) ;
    }

}

3、创建主题配置类(如果服务器中已经存在topic-01,那么消费者端可以不配置这个Bean)

package com.atguigu.kafka.config;
@Configuration
public class KafkaConfig {

    @Bean
    public NewTopic springTestTopic(){
        return TopicBuilder.name("topic-01") //主题名称, 该主题不存在直接创建,如果存在就复用
                .partitions(3) //分区数量
                .replicas(3) //副本数量
                .build();
    }
}

4、在application.yml文件中添加如下配置,对消费者进行配置:

server:
  port: 8120

# 消费者配置
spring:
  Kafka:
    bootstrap-servers: 192.168.100.102:9092,192.168.100.102:9093,192.168.100.102:9094
    consumer: # consumer消费者配置
      group-id: group03 # 默认的消费组ID
      enable-auto-commit: true # 消费者是否进行自动offset提交
      auto-commit-interval: 5000 # 消费者自动提交offset时间间隔为5s。这期间如果kafka服务异常停止时,再次重启消费者服务会导致消息重复消费(enable-auto-commit改成false后这个属性会失效)
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer #配置反序列化器,和传统Java API配置反序列化器是一样的
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

4.2.2 消费消息

package com.atguigu.kafka.listener;
@Component
public class KafkaListeners {

    /**
     * 简单消费:
     */
    @KafkaListener(topics = {"topic-01"})  topics属性:指定此消费者要监听的主题列表
    //ConsumerRecord<String, String> record: 记录对象,封装消息的相关数据,加上两个String泛型那么record.value拿到的消息内容就是String类型,否则就是Object
    public void simpleConsumer(ConsumerRecord<String, String> record ) {
        System.out.println("进入simpleConsumer方法");
        System.out.printf(
                "分区 = %d, 偏移量 = %d, key = %s, 消息的内容 = %s, 时间戳 = %d%n",
                record.partition(),
                record.offset(),
                record.key(),
                record.value(),   //消息发送的内容
                record.timestamp() 
        );
    }

}

最后启动生产者和消费者服务

4.2.3 消费者的手动位移提交

修改消费者的yml文件配置:

server:
  port: 8120

# 消费者配置
spring:
  Kafka:
    bootstrap-servers: 192.168.100.102:9092,192.168.100.102:9093,192.168.100.102:9094
    listener:
    	ack-mode: manual_immediate   #手动(设置消费者的手动位移提交,添加这个!!!!!)
    consumer: # consumer消费者配置
      group-id: group03 # 默认的消费组ID
      enable-auto-commit: false # 关闭offset自动提交(设置消费者的手动位移提交,这里修改改成false!!!!)
      auto-commit-interval: 5000 # 消费者自动提交offset时间间隔为5s。这期间如果kafka服务异常停止时,再次重启消费者服务会导致消息重复消费(enable-auto-commit改成false后这个属性会失效)
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

修改消费者接收消息的代码:

package com.atguigu.kafka.listener;
@Component
public class KafkaListeners {

    /**
     * 简单消费:
     * topics:要监听的主题列表
     * ConsumerRecord<String, String> record: 记录对象,封装消息记录的相关数据
     */
    @KafkaListener(topics = {"topic-01"})
    public void simpleConsumer(ConsumerRecord<String, String> record,Consumer consumer) {
        System.out.println("进入simpleConsumer方法");
        System.out.printf(
                "分区 = %d, 偏移量 = %d, key = %s, 内容 = %s, 时间戳 = %d%n",
                record.partition(),
                record.offset(),
                record.key(),
                record.value(),   //消息发送的内容
                record.timestamp() 
        );
        //消费者消息消费完毕以后,进行手动位移提交
        consumer.commitAsync();
    }

}

5、生产者测试

package com.atguigu.kafka.test
    
@SpringBootTest(classes = ProducerApplication.class)
public class ProducerApplicationTest {

    @Autowired
    private KafkaTemplate kafkaTemplate ;

    @Test
    public void send() {
       for(int i = 0;i < 5;i++){
            kafkaTemplate.send("topic-01" , "kafka...producer...send...message..." + i) ;
       }
    }

}

4.2.4 消费时的异常处理

消费者 消费 消息时,如果发生了异常需要处理异常。一般我们在@KafkaListener中,只是监听topic中的主题并消费,如果再try catch捕获并处理的话,则会显得代码块非常臃肿不利于维护。

为此,kafka为我们提供了专门的异常处理器ConsumerAwareListenerErrorHandler通过它我们可以处理consumer在消费时发生的异常。

具体使用步骤如下所示:

1、定义配置类,并在其中声明ConsumerAwareListenerErrorHandler这个Bean即可。

package com.atguigu.kafka.config;
@Configuration
public class CustomListenerErrorHandler {

    @Bean
    public ConsumerAwareListenerErrorHandler listenerErrorHandler(){
        return (Message<?> message, ListenerExecutionFailedException exception, Consumer<?, ?> consumer) -> {
            System.out.println("--- 消费时发生异常 ---");
            return null;
        } ;
    }

}

2、在消费者中,使用@KafkaListener(topics = {"topic-01"})注解的errorHandler属性指定使用的异常处理器(异常处理器的id)

  • 在消费者中,在提交offset之前发生异常时,就会跳转到消费者的异常处理器中执行

5.Kafka的高级内容

参考这篇文章:https://flepeng.github.io/043-Kafka-Kafka-%E6%9C%80%E5%A4%9A%E4%B8%80%E6%AC%A1%E3%80%81%E8%87%B3%E5%B0%91%E4%B8%80%E6%AC%A1%E3%80%81%E7%B2%BE%E7%A1%AE%E4%BC%A0%E9%80%92%E4%B8%80%E6%AC%A1/

补充:docker-compose文件说明

docker-compose.yml 文件定义了一个包含 Zookeeper 和三个 Kafka 实例以及一个管理工具 (Kafka Eagle) 的多服务 Docker Compose 配置。以下是对每个部分的逐行解释:

文件版本

version: '3'
  • version: ‘3’:指定使用 Docker Compose 文件格式的版本 3。这个版本支持多种功能,适用于定义多服务应用。

服务定义

Zookeeper 服务
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
      - 2181:2181
    volumes:
      - "zookeeper-data:/data"
      - "zookeeper-datalog:/datalog"
      - "zookeeper-logs:/logs"
    restart: always
  • services:定义 Docker Compose 文件中的服务。

  • zookeeper:服务名称,包含 Zookeeper ,用于管理 Kafka 集群的元数据。

  • image: wurstmeister/zookeeper:使用 wurstmeister/zookeeper 镜像。

  • container_name: zookeeper:容器命名为 zookeeper

  • ports

    • 2181:2181:将主机 2181 端口映射到容器 2181 端口。

  • volumes

    • "zookeeper-data:/data":挂载名为 zookeeper-data 的卷到容器内的 /data 目录。

    • "zookeeper-datalog:/datalog":挂载名为 zookeeper-datalog 的卷到容器内的 /datalog 目录。

    • "zookeeper-logs:/logs":挂载名为 zookeeper-logs 的卷到容器内的 /logs 目录。

  • restart: always:docker重启后容器总是重启。

  • networks

    • cluster_net:将服务连接到名为 cluster_net 的网络。

Kafka 实例
  kafka1:
    image: wurstmeister/kafka
    depends_on:
      - zookeeper
    container_name: kafka1
    ports:
      - "9092:9092"
    environment:
      - "KAFKA_BROKER_ID=1"
      - "KAFKA_ZOOKEEPER_CONNECT=192.168.100.102:2181"
      - "KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.100.102:9092"
      - "KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092"
      - "KAFKA_LOG_DIRS=/data/kafka-data"
    volumes:
      - "kafka1-data:/data/kafka-data"
      - "kafka1-config:/opt/kafka/config"
    restart: always
  • kafka1:第一个 Kafka 服务实例。

  • depends_on:

    • zookeeper:表示该服务依赖于 zookeeper 服务。

  • container_name: kafka1:容器命名为 kafka1

  • ports:

    • "9092:9092":将主机 9092 端口映射到容器 9092 端口。

  • environment:

    • "KAFKA_BROKER_ID=1":Kafka broker 的 ID 为 1。

    • "KAFKA_ZOOKEEPER_CONNECT=192.168.100.102:2181":指定 Zookeeper 的连接地址和端口。

    • "KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.100.102:9092":客户端链接 Kafka 的地址。

    • "KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092":表示 Kafka broker 在所有网络接口的 9092 端口上监听连接,用于Kafka 集群的内部通信。

    • "KAFKA_LOG_DIRS=/data/kafka-data":Kafka 日志目录为 /data/kafka-data

  • volumes:

    • "kafka1-data:/data/kafka-data":挂载名为 kafka1-data 的卷到容器内的 /data/kafka-data 目录。

    • "kafka1-config:/opt/kafka/config":挂载名为 kafka1-config 的卷到容器内的 /opt/kafka/config 目录。

  • restart: always:docker重启后容器总是重启。

Kafka Eagle 服务
  eagle:
    container_name: eagle
    image: nickzurich/efak:latest
    restart: always
    ports:
      - "8048:8048"
    environment:
      - "EFAK_CLUSTER_ZK_LIST=192.168.100.102:2181"
    volumes:
      - "kafka-eagle:/hadoop/kafka-eagle/db"
  • eagle:Kafka Eagle 服务,一个用于 Kafka 集群管理的工具。

  • container_name: eagle:容器命名为 eagle

  • image: nickzurich/efak:latest:使用 nickzurich/efak:latest 镜像。

  • restart: always:docker重启后容器总是重启。

  • ports:

    • "8048:8048":将主机 8048 端口映射到容器 8048 端口。

  • environment:

    • "EFAK_CLUSTER_ZK_LIST=192.168.100.102:2181":配置 Kafka Eagle 连接到 Zookeeper 的地址。

  • volumes:

    • "kafka-eagle:/hadoop/kafka-eagle/db":挂载名为 kafka-eagle 的卷到容器内的 /hadoop/kafka-eagle/db 目录。

数据卷的定义

volumes:
  kafka1-data: {}
  kafka2-data: {}
  kafka3-data: {}
  zookeeper-data: {}
  zookeeper-datalog: {}
  zookeeper-logs: {}
  kafka-eagle: {}
  kafka1-config: {}
  kafka2-config: {}
  kafka3-config: {}
  • volumes:定义容器会使用的命名卷。

  • kafka1-datakafka2-datakafka3-datazookeeper-datazookeeper-datalogzookeeper-logskafka-eaglekafka1-configkafka2-configkafka3-config:各个自定义卷用于持久化存储 Kafka 和 Zookeeper 的数据和配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值