使用Spring Kafka添加自定义标头

时间:2018-02-15 00:00:36

标签: spring apache-kafka spring-kafka spring-boot-test

我计划使用Spring Kafka客户端在Spring Boot应用程序中使用和生成来自kafka设置的消息。我在Kafka 0.11中看到了对详细here的自定义标头的支持。虽然它可供本地Kafka制作人和消费者使用,但我没有看到在Spring Kafka中添加/阅读自定义标题的支持。

我正在尝试基于我希望存储在邮件头中的重试计数来实现消息的DLQ,而不必解析有效负载。

2 个答案:

答案 0 :(得分:3)

好吧,Spring Kafka提供了自 2.0版以来的标头支持:https://docs.spring.io/spring-kafka/docs/2.1.2.RELEASE/reference/html/_reference.html#headers

您可以拥有该KafkaHeaderMapper个实例,并在通过Message发送之前将其用于填充KafkaTemplate.send(Message<?> message)标题。或者您可以使用普通KafkaTemplate.send(ProducerRecord<K, V> record)

当您使用KafkaMessageListenerContainer接收记录时,可以通过注入KafkaHeaderMapper的{​​{1}}向MessagingMessageConverter提供RecordMessagingMessageListenerAdapter

因此,任何自定义标头都可以转移。

答案 1 :(得分:1)

当我偶然发现这个问题时,我正在寻找答案。但是,我使用的是ProducerRecord<?, ?>类而不是Message<?>,因此标头映射器似乎并不相关。

这是我添加自定义标头的方法:

var record = new ProducerRecord<String, String>(topicName, "Hello World");
record.headers().add("foo", "bar".getBytes());
kafkaTemplate.send(record);

现在(在使用之前)读取标头,我添加了一个自定义拦截器。

import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerInterceptor;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;

@Slf4j
public class MyConsumerInterceptor implements ConsumerInterceptor<Object, Object> {

    @Override
    public ConsumerRecords<Object, Object> onConsume(ConsumerRecords<Object, Object> records) {
        Set<TopicPartition> partitions = records.partitions();
        partitions.forEach(partition -> interceptRecordsFromPartition(records.records(partition)));

        return records;
    }

    private void interceptRecordsFromPartition(List<ConsumerRecord<Object, Object>> records) {
        records.forEach(record -> {
            var myHeaders = new ArrayList<Header>();
            record.headers().headers("MyHeader").forEach(myHeaders::add);
            log.info("My Headers: {}", myHeaders);
            // Do with header as you see fit
        });
    }

    @Override public void onCommit(Map<TopicPartition, OffsetAndMetadata> offsets) {}
    @Override public void close() {}
    @Override public void configure(Map<String, ?> configs) {}
}

最后一点是使用以下(Spring Boot)配置将此拦截器注册到Kafka Consumer Container:

import java.util.Map;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;

@Configuration
public class MessagingConfiguration {

    @Bean
    public ConsumerFactory<?, ?> kafkaConsumerFactory(KafkaProperties properties) {
        Map<String, Object> consumerProperties = properties.buildConsumerProperties();
        consumerProperties.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, MyConsumerInterceptor.class.getName());
        return new DefaultKafkaConsumerFactory<>(consumerProperties);
    }

}