Java Spring for Kafka 2.3使用KafkaMessageListenerContainer在运行时为特定侦听器设置偏移量

Java Spring for Kafka 2.3使用KafkaMessageListenerContainer在运行时为特定侦听器设置偏移量,java,spring,apache-kafka,spring-kafka,Java,Spring,Apache Kafka,Spring Kafka,我必须实现一个功能,将某个主题/分区的侦听器(重新)设置为任何给定的偏移量。因此,如果将事件提交到偏移量5,并且管理员决定将偏移量重置为2,则应重新处理事件3、4和5 我们正在为卡夫卡2.3使用Spring,我试图遵循文档,这似乎正是我想要的 然而,问题是我们使用的主题也是在运行时创建的。为此,我们通过DefaultKafkaConsumerFactory使用KafkaMessageListenerContainer,我不知道将注册表seekcallback或类似的东西放在哪里 有没有办法做到这

我必须实现一个功能,将某个主题/分区的侦听器(重新)设置为任何给定的偏移量。因此,如果将事件提交到偏移量5,并且管理员决定将偏移量重置为2,则应重新处理事件3、4和5

我们正在为卡夫卡2.3使用Spring,我试图遵循文档,这似乎正是我想要的

然而,问题是我们使用的主题也是在运行时创建的。为此,我们通过
DefaultKafkaConsumerFactory
使用
KafkaMessageListenerContainer
,我不知道将注册表seekcallback或类似的东西放在哪里

有没有办法做到这一点?我无法理解使用
@KafkaListener
注释的类如何映射到工厂中创建侦听器的方式

任何帮助都将不胜感激。即使这只是解释这些东西是如何一起工作的

这就是Kafka消息传递容器的基本创建方式:

public KafkaMessageListenerContainer<String, Object> createKafkaMessageListenerContainer(String topicName,
        ContainerPropertiesStrategy containerPropertiesStrategy) {
    MessageListener<String, String> messageListener = getMessageListener(topicName);

    ConsumerFactory<String, Object> consumerFactory = new DefaultKafkaConsumerFactory<>(getConsumerFactoryConfiguration());

    KafkaMessageListenerContainer<String, Object> kafkaMessageListenerContainer = createKafkaMessageListenerContainer(topicName, messageListener, bootstrapServers, containerPropertiesStrategy, consumerFactory);
    return kafkaMessageListenerContainer;
}

public MessageListener<String, String> getMessageListener(String topic) {
    MessageListener<String, String> messageListener = new MessageListener<String, String>() {

        @Override
        public void onMessage(ConsumerRecord<String, String> message) {
            try {
                consumerService.consume(topic, message.value());
            } catch (IOException e) {
                log.log(Level.WARNING, "Message couldn't be consumed", e);
            }
        }
    };
    return messageListener;
}

public static KafkaMessageListenerContainer<String, Object> createKafkaMessageListenerContainer(
  String topicName, MessageListener<String, String> messageListener, String bootstrapServers, ContainerPropertiesStrategy containerPropertiesStrategy,
  ConsumerFactory<String, Object> consumerFactory) {
ContainerProperties containerProperties = containerPropertiesStrategy.getContainerPropertiesForTopic(topicName);
containerProperties.setMessageListener(messageListener);

KafkaMessageListenerContainer<String, Object> kafkaMessageListenerContainer = new KafkaMessageListenerContainer<>(
    consumerFactory, containerProperties);
kafkaMessageListenerContainer.setBeanName(topicName);
return kafkaMessageListenerContainer;
}
public KafkaMessageListenerContainer创建KafkaMessageListenerContainer(字符串topicName,
集装箱财产战略(集装箱财产战略){
MessageListener MessageListener=getMessageListener(topicName);
ConsumerFactory ConsumerFactory=新的默认KafkanConsumerFactory(getConsumerFactoryConfiguration());
KafkaMessageListenerContainer KafkaMessageListenerContainer=createKafkaMessageListenerContainer(主题名称、消息侦听器、引导服务器、容器属性策略、消费者工厂);
返回KafkamessageliesStenerContainer;
}
public MessageListener getMessageListener(字符串主题){
MessageListener MessageListener=newmessagelistener(){
@凌驾
消息(消费者记录消息)上的公共无效{
试一试{
consumerService.consume(主题,message.value());
}捕获(IOE异常){
log.log(Level.WARNING,“无法使用消息”,e);
}
}
};
返回消息侦听器;
}
公共静态KafkaMessageListenerContainer创建KafkaMessageListenerContainer(
字符串topicName、MessageListener MessageListener、字符串BootstrapServer、ContainerPropertiesStrategy ContainerPropertiesStrategy、,
消费者工厂(消费者工厂){
ContainerProperties ContainerProperties=containerPropertiesStrategy.getContainerPropertiesForTopic(topicName);
containerProperties.setMessageListener(messageListener);
KafkCamessageListenerContainer KafkCamessageListenerContainer=新的KafkCamessageListenerContainer(
消费者工厂、集装箱房地产);
kafkaMessageListenerContainer.setBeanName(主题名);
返回KafkamessageliesStenerContainer;
}

希望这能有所帮助。

我认为您可以像这样为spring kafka使用一些注释,尽管在运行时在注释中设置偏移量可能会很尴尬

    @KafkaListener(topicPartitions =
    @TopicPartition(topic = "${kafka.consumer.topic}", partitionOffsets = {
            @PartitionOffset(partition = "0", initialOffset = "2")}),
            containerFactory = "filterKafkaListenerContainerFactory", id = "${kafka.consumer.groupId}")
    public void receive(ConsumedObject event) {
        log.info(String.format("Consumed message with correlationId: %s", event.getCorrelationId()));
        consumerHelper.start(event);
    }
或者,这里是我编写的一些代码,用于从给定的偏移量中使用,我模拟了消费者在消息上失败,这是使用KafkanConsumer,而不是KafkaMessageListenerContainer

    private static void ConsumeFromOffset(KafkaConsumer<String, Customer> consumer, boolean flag, String topic) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter offset: ");
        int offsetInput = scanner.nextInt();

        while (true) {
            ConsumerRecords<String, Customer> records = consumer.poll(500);

            for (ConsumerRecord<String, Customer> record : records) {
                Customer customer = record.value();
                System.out.println(customer + " has offset ->" + record.offset());
                if (record.offset() == 7 && flag) {
                    System.out.println("simulating consumer failing after offset 7..");
                    break;
                }
            }
            consumer.commitSync();

            if (flag) {
                // consumer.seekToBeginning(Stream.of(new TopicPartition(topic, 0)).collect(Collectors.toList())); // consume from the beginning
                consumer.seek(new TopicPartition(topic, 0), 3); // consume
                flag = false;
            }
        }
    }
private static void consumerfromfomoffset(KafkaConsumer consumer,布尔标志,字符串主题){
扫描仪=新的扫描仪(System.in);
系统输出打印(“输入偏移:”);
int offsetInput=scanner.nextInt();
while(true){
ConsumerRecords记录=consumer.poll(500);
对于(消费者记录:记录){
Customer=record.value();
System.out.println(客户+“有偏移量->”+记录.offset());
if(record.offset()==7&&flag){
System.out.println(“偏移量7后模拟耗电元件故障”);
打破
}
}
consumer.commitSync();
国际单项体育联合会(旗){
//consumer.seektobegining(Stream.of(new-TopicPartition(topic,0)).collect(Collectors.toList());//从头开始消费
consumer.seek(新的主题分区(主题,0),3);//consumer
flag=false;
}
}
}

关键组件是
AbstractConsumerSekAware
。希望这将提供足够的,让你开始

@springboot应用程序
公共类SO59682801应用程序{
公共静态void main(字符串[]args){
run(So59682801Application.class,args.close();
}
@豆子
公共应用程序运行程序(ListenerCreator,
KafkaTemplate模板,通用应用程序上下文){
返回参数->{
System.out.println(“按enter键创建侦听器”);
System.in.read();
ConcurrentMessageListenerContainer容器=
createContainer(“so59682801组”、“so59682801”);
//将容器注册为bean,以便满足所有“…感知”接口
registerBean(“so59682801”,ConcurrentMessageListenerContainer.class,()->container);
context.getBean(“so59682801”,ConcurrentMessageListenerContainer.class);//重新获取以初始化
container.start();
//发送一些消息
IntStream.range(0,10).forEach(i->template.send(“so59682801”,“test”+i));
System.out.println(“点击回车重新设定”);
System.in.read();
((MyListener)container.getContainerProperties().getMessageListener())
.reseek(新主题分区(“so59682801”,0),5L);
System.out.println(“点击回车退出”);
System.in.read();
};
}
}
@组成部分
类ListenerCreator{
私人最终同意卡夫卡利斯特集装箱工厂;
ListenerCreator(ConcurrentKafkaListenerContainerFactory工厂){
fa