跟着官方文档学Spring Boot - 集成Kafka

932 阅读3分钟

跟着官方文档学Spring Boot - 集成Kafka

这将会是一系列的文档,文章中大部分的内容都来自 SpringBoot 2.7.0的官方文档,如果需要则会提供一个最简单的环境搭建方案,每一篇文章的开头我都会列出参考的官方文档的地址,如果可以,请尽可能的阅读原文。

原文链接: Spring Boot Apache Kafka Support

一、 准备环境(如果有部署好的 kafka 请跳过)

将下面的内容保存到 docker-compose.yml 中使用 docker-compose up -d 在本地启动一个kafka

version: "2"
networks:
  legacy:
services:
  zookeeper:
    networks:
      - legacy
    image: docker.io/bitnami/zookeeper:3.8
    ports:
      - "2181:2181"
    volumes:
      - "zookeeper_data:/bitnami"
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes
  kafka:
    image: docker.io/bitnami/kafka:3.2
    networks:
      legacy:
        aliases: 
          - kafka-network
    ports:
      - "9092:9092"
    volumes:
      - "kafka_data:/bitnami"
    environment:
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092
      - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092
    depends_on:
      - zookeeper
volumes:
  zookeeper_data:
    driver: local
  kafka_data:
    driver: local

二、修改代码

Kafka
  1. 添加依赖
implementation 'org.springframework.kafka:spring-kafka'
  1. application.properties 增加配置
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup
  1. 发送消息

Spring 的 KafkaTemplate 是自动配置的, 你可以直接在你的bean中注入它。

@Component
@Slf4j
@RequiredArgsConstructor
public class KafkaService {
    private final KafkaTemplate<String, String> kafkaTemplate;

    public boolean sendMessage(String message) {
        final ListenableFuture<SendResult<String, String>> sendResult = kafkaTemplate.send("test_topic", message);
        try {
            sendResult.get();
            return true;
        } catch (InterruptedException | ExecutionException e) {
            log.error("kafka 消息发送失败", e);
            return false;
        }
    }
}
  1. 接收消息

可以对任何一个bean标记@KafkaListener注解来创建一个监听器

@Component
@Slf4j
@RequiredArgsConstructor
public class KafkaService {
    private final KafkaTemplate<String, String> kafkaTemplate;
  
    @KafkaListener(topics = "test_topic")
    public void onMessageReceived(String message) {
        log.info("收到消息:{}", message);
    }
}

@KafkaListener 标记的方法可以增加的参数有:

来自于 @KafkaListener源码注释

org.apache.kafka.clients.consumer.ConsumerRecord to access to the raw Kafka message

org.springframework.kafka.support.Acknowledgment to manually ack

@Payload-annotated method arguments including the support of validation

@Header-annotated method arguments to extract a specific header value, defined by KafkaHeaders

@Headers-annotated argument that must also be assignable to java.util.Map for getting access to all headers.

MessageHeaders arguments for getting access to all headers.

MessageHeaderAccessor for convenient access to all method arguments.

Kafka Streams

Spring for Apache Kafka提供了一个factory bean 来创建StreamsBuilder对象并管理流的生命周期。当类路径上有kafka-streams并且KafkaStreams由注解@EnableKafkaStreams启用时,Spring会自动配置需要的KafkaStreamsConfigurationbean

启用Kafka Streams 意味着 application id 和 bootstrap servers 必须配置。前者(application id )如果spring.kafka.streams.application-id没有配置,默认使用 spring.application.name

使用专用属性提供了其他几个属性;可以使用spring.kafka.streams.properties命名空间设置其他任意Kafka属性。有关更多信息,请参阅Additional Kafka Properties

  1. 添加依赖
implementation 'org.apache.kafka:kafka-streams'
  1. 增加配置
spring.kafka.streams.properties.default.key.serde=org.apache.kafka.common.serialization.Serdes$IntegerSerde
spring.kafka.streams.properties.default.value.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
  1. 增加 bean 配置
@Configuration(proxyBeanMethods = false)
@EnableKafkaStreams
@Slf4j
public class MyKafkaStreamsConfiguration {
    @Bean
    public KStream<Integer, String> kStream(StreamsBuilder streamsBuilder) {
        KStream<Integer, String> stream = streamsBuilder.stream("ks1In");
        stream.map(this::uppercaseValue).to("ks1Out", Produced.with(Serdes.Integer(),
                new JsonSerde<>()));
        return stream;
    }

    private KeyValue<Integer, String> uppercaseValue(Integer key, String value) {
        return new KeyValue<>(key, value.toUpperCase());
    }
}

上述代码会从 名为 ks1In topic 导入数据,之后执行 uppercaseValue 方法,将消息的内容转换为大写,之后写回到 ks1Out 这个topic

使用内嵌的Kafka测试

Spring 为 Apache Kafka 提供了一种使用嵌入式Apache Kafka broker 做测试的便捷方法。要使用此功能,请使用Spring-kafka测试模块中的@EmbeddedKafka注释测试类。

要使Spring Boot自动配置与嵌入式Apache Kafka一起工作,嵌入式Kafka broker 地址 重新映射到Apache Kafka的Spring Boot配置属性中。(官方文档提供了好几种,这里只列出一种,更详细的内容可以参考官方文档)

在测试类中增加如下代码:

static {
        System.setProperty(EmbeddedKafkaBroker.BROKER_LIST_PROPERTY,
                "spring.kafka.bootstrap-servers");
    }

完整的测试类代码如下:

@SpringBootTest
@EmbeddedKafka
class KafkaServiceTest {
    @Autowired
    private KafkaService service;

    static {
        System.setProperty(EmbeddedKafkaBroker.BROKER_LIST_PROPERTY,
                "spring.kafka.bootstrap-servers");
    }

    @Test
    void sendMessage() {
        Assert.isTrue(service.sendMessage("test_topic", "test message"), "测试消息发送");
    }    
}

**嵌入式的Kafka仅能在编写测试代码时使用 **