Spring集成Apache Kafka

1.概述
Apache Kafka是一个分布式且容错的流处理系统。

在本文中,我们将介绍Spring对Kafka的支持及其在本机Kafka Java客户端API上提供的抽象。

Spring Kafka 通过@KafkaListener注解为KafkaTemplate和消息驱动的POJO 带来了简单而典型的Spring模板编程模型。

2.安装和设置
要下载并安装Kafka,请参阅此处的官方指南。

我们还需要将spring-kafka依赖项添加到我们的pom.xml中:

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
    <version>2.3.7.RELEASE</version>
</dependency>

可以在此处找到此工件的最新版本。

我们的示例应用程序将是一个Spring Boot应用程序。

本文假定使用默认配置启动服务器,并且未更改服务器端口。

3.配置主题
以前,我们曾经运行命令行工具来在Kafka中创建主题,例如:

$ bin/kafka-topics.sh --create \
  --zookeeper localhost:2181 \
  --replication-factor 1 --partitions 1 \
  --topic mytopic

但是随着 Kafka 中AdminClient的引入,我们现在可以以编程方式创建主题。

我们需要添加KafkaAdmin Spring bean,它将为所有NewTopic类型的bean添加主题:

@Configuration
public class KafkaTopicConfig {
    
    @Value(value = "${kafka.bootstrapAddress}")
    private String bootstrapAddress;
 
    @Bean
    public KafkaAdmin kafkaAdmin() {
        Map<String, Object> configs = new HashMap<>();
        configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
        return new KafkaAdmin(configs);
    }
    
    @Bean
    public NewTopic topic1() {
         return new NewTopic("baeldung", 1, (short) 1);
    }
}

4.产生信息
要创建消息,首先,我们需要配置一个ProducerFactory,该工厂设置创建Kafka Producer实例的策略。

然后,我们需要一个KafkaTemplate,它包装了Producer实例并提供了将消息发送到Kafka主题的便捷方法。

生产者实例是线程安全的,因此在整个应用程序上下文中使用单个实例将提供更高的性能。因此,KakfaTemplate实例也是线程安全的,建议使用一个实例。
4.1 生产者配置


@Configuration
public class KafkaProducerConfig {
 
    @Bean
    public ProducerFactory<String, String> producerFactory() {
        Map<String, Object> configProps = new HashMap<>();
        configProps.put(
          ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, 
          bootstrapAddress);
        configProps.put(
          ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, 
          StringSerializer.class);
        configProps.put(
          ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 
          StringSerializer.class);
        return new DefaultKafkaProducerFactory<>(configProps);
    }
 
    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
}

4.2 发布消息
我们可以使用KafkaTemplate类发送消息:

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
 
public void sendMessage(String msg) {
    kafkaTemplate.send(topicName, msg);
}

在发送 API返回ListenableFuture对象。如果要阻止发送线程并获取有关已发送消息的结果,则可以调用ListenableFuture对象的get API 。线程将等待结果,但会减慢生产者的速度。

Kafka是一个快速流处理平台。因此,最好以异步方式处理结果,以使后续消息不再等待前一条消息的结果。我们可以通过回调来做到这一点:

public void sendMessage(String message) {
            
    ListenableFuture<SendResult<String, String>> future = 
      kafkaTemplate.send(topicName, message);
	
    future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
 
        @Override
        public void onSuccess(SendResult<String, String> result) {
            System.out.println("Sent message=[" + message + 
              "] with offset=[" + result.getRecordMetadata().offset() + "]");
        }
        @Override
        public void onFailure(Throwable ex) {
            System.out.println("Unable to send message=[" 
              + message + "] due to : " + ex.getMessage());
        }
    });
}

5.消费信息
5.1 消费者配置
为了使用消息,我们需要配置一个ConsumerFactory和KafkaListenerContainerFactory。一旦这些bean在Spring bean工厂中可用,就可以使用@KafkaListener注释配置基于POJO的使用者。

配置类上需要@EnableKafka注释,以便在 spring 托管 bean 上检测@KafkaListener注释:

@EnableKafka
@Configuration
public class KafkaConsumerConfig {
 
    @Bean
    public ConsumerFactory<String, String> consumerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(
          ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, 
          bootstrapAddress);
        props.put(
          ConsumerConfig.GROUP_ID_CONFIG, 
          groupId);
        props.put(
          ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
          StringDeserializer.class);
        props.put(
          ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
          StringDeserializer.class);
        return new DefaultKafkaConsumerFactory<>(props);
    }
 
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> 
      kafkaListenerContainerFactory() {
   
        ConcurrentKafkaListenerContainerFactory<String, String> factory =
          new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        return factory;
    }
}

5.2 消费信息

@KafkaListener(topics = "topicName", groupId = "foo")
public void listen(String message) {
    System.out.println("Received Messasge in group foo: " + message);
}

可以为一个主题实现多个侦听器,每个侦听器具有不同的组ID。此外,一个消费者可以收听来自各种主题的消息:

@KafkaListener(topics = "topic1, topic2", groupId = "foo")

Spring还支持在侦听器中使用@Header批注来检索一个或多个消息头:

@KafkaListener(topics = "topicName")
public void listenWithHeaders(
  @Payload String message, 
  @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
      System.out.println(
        "Received Message: " + message"
        + "from partition: " + partition);
}

5.3 消费来自特定分区的消息
您可能已经注意到,我们仅用一个分区创建了主题baeldung。但是,对于具有多个分区的主题,@ KafkaListener可以显式订阅具有初始偏移量的主题的特定分区:

@KafkaListener(
  topicPartitions = @TopicPartition(topic = "topicName",
  partitionOffsets = {
    @PartitionOffset(partition = "0", initialOffset = "0"), 
    @PartitionOffset(partition = "3", initialOffset = "0")}),
  containerFactory = "partitionsKafkaListenerContainerFactory")
public void listenToParition(
  @Payload String message, 
  @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
      System.out.println(
        "Received Messasge: " + message"
        + "from partition: " + partition);
}

由于已在此侦听器中将initialOffset发送为0,因此每次初始化此侦听器时,都会重新使用分区0和分区3先前消耗的所有消息。如果不需要设置偏移量,则可以使用@TopicPartition批注的partitions属性来仅设置没有偏移量的分区:

@KafkaListener(topicPartitions 
  = @TopicPartition(topic = "topicName", partitions = { "0", "1" }))

5.4 为侦听器添加消息过滤器
通过添加自定义过滤器,可以将侦听器配置为使用特定类型的消息。这可以通过将RecordFilterStrategy设置为KafkaListenerContainerFactory来完成:

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String>
  filterKafkaListenerContainerFactory() {
 
    ConcurrentKafkaListenerContainerFactory<String, String> factory =
      new ConcurrentKafkaListenerContainerFactory<>();
    factory.setConsumerFactory(consumerFactory());
    factory.setRecordFilterStrategy(
      record -> record.value().contains("World"));
    return factory;
}

然后可以将侦听器配置为使用此容器工厂:

@KafkaListener(
  topics = "topicName", 
  containerFactory = "filterKafkaListenerContainerFactory")
public void listen(String message) {
    // handle message
}

在此侦听器中,所有与过滤器匹配的消息都将被丢弃。

6.自定义消息转换器
到目前为止,我们仅介绍了将String作为消息发送和接收。但是,我们也可以发送和接收自定义Java对象。这需要在ProducerFactory中配置适当的序列化器,在ConsumerFactory中配置反序列化器。

让我们看一个简单的bean类,它将作为消息发送:

public class Greeting {
 
    private String msg;
    private String name;
 
    // standard getters, setters and constructor
}

6.1 产生自定义消息
在此示例中,我们将使用JsonSerializer。让我们看一下ProducerFactory和KafkaTemplate的代码:

@Bean
public ProducerFactory<String, Greeting> greetingProducerFactory() {
    // ...
    configProps.put(
      ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 
      JsonSerializer.class);
    return new DefaultKafkaProducerFactory<>(configProps);
}
 
@Bean
public KafkaTemplate<String, Greeting> greetingKafkaTemplate() {
    return new KafkaTemplate<>(greetingProducerFactory());
}

这个新的KafkaTemplate可用于发送Greeting消息:

kafkaTemplate.send(topicName, new Greeting("Hello", "World"));

6.2 消费自定义消息
同样,让我们​​修改ConsumerFactory和KafkaListenerContainerFactory以正确反序列化Greeting消息:

@Bean
public ConsumerFactory<String, Greeting> greetingConsumerFactory() {
    // ...
    return new DefaultKafkaConsumerFactory<>(
      props,
      new StringDeserializer(), 
      new JsonDeserializer<>(Greeting.class));
}

@Bean
public ConcurrentKafkaListenerContainerFactory<String, Greeting> 
  greetingKafkaListenerContainerFactory() {

    ConcurrentKafkaListenerContainerFactory<String, Greeting> factory =
      new ConcurrentKafkaListenerContainerFactory<>();
    factory.setConsumerFactory(greetingConsumerFactory());
    return factory;
}

spring-kafka JSON序列化器和反序列化器使用Jackson库,该库也是spring-kafka项目的可选maven依赖项。因此,将其添加到我们的pom.xml中:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.7</version>
</dependency>

建议不要使用添加到spring-kafka 的pom.xml中的版本,而不要使用最新版本的Jackson 。

最后,我们需要编写一个侦听器来使用Greeting消息:

@KafkaListener(
  topics = "topicName", 
  containerFactory = "greetingKafkaListenerContainerFactory")
public void greetingListener(Greeting greeting) {
    // process greeting message
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot可以轻松地集成Kafka,使得应用程序可以轻松地使用Kafka进行消息传递。 以下是Spring Boot集成Kafka的步骤: 1. 添加Kafka依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> ``` 2. 创建Kafka配置 在application.properties文件中添加以下配置: ``` spring.kafka.bootstrap-servers=localhost:9092 spring.kafka.consumer.group-id=my-group spring.kafka.consumer.auto-offset-reset=earliest spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer ``` 3. 创建生产者 创建一个Kafka生产者,用于将消息发送到Kafka中: ``` @Service public class KafkaProducerService { @Autowired private KafkaTemplate<String, String> kafkaTemplate; public void sendMessage(String topic, String message) { kafkaTemplate.send(topic, message); } } ``` 4. 创建消费者 创建一个Kafka消费者,用于从Kafka中接收消息: ``` @Service public class KafkaConsumerService { @KafkaListener(topics = "my-topic") public void listen(String message) { System.out.println("Received message: " + message); } } ``` 5. 发送和接收消息 在应用程序中使用KafkaProducerService发送消息,使用KafkaConsumerService接收消息: ``` @Autowired private KafkaProducerService kafkaProducerService; public void sendMessage() { kafkaProducerService.sendMessage("my-topic", "Hello Kafka!"); } ``` 6. 运行应用程序 运行应用程序,发送和接收Kafka消息。 以上就是Spring Boot集成Kafka的步骤。通过使用Spring BootKafka,可以轻松地构建可扩展的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值