spring-kafka:如果我在我的应用程序中定义记录生成器,Kafka使用者不会收到记录

时间:2017-12-01 08:21:54

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

我正在使用Spring和Spring Kafka编写一个小型PoC。我的目标是同时拥有一个制作人和一个消费者,从这个主题写作(再读)。

我遇到了一个奇怪的情况:

  • 生产者正确地生成记录(我可以通过Python脚本使用它们)
  • 消费者未收到记录
  • 如果我从代码中删除生产者并通过其他方法生成记录(例如使用python脚本),则消费者会正确接收记录。

以下是我的代码 - 它与文档示例非常相似。更确切地说,问题来自于 KafkaConsumerConfiguration 中的bean不是由Spring创建的(即从不调用构造它们的方法)。

生产者

KafkaProducerConfiguration.java

@Configuration
public class KafkaProducerConfiguration {

    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }

    @Bean
    public ProducerFactory<String, String> producerFactory() {
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:32768");
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return new DefaultKafkaProducerFactory<>(props);
    }
}

MessageSender.java

@Component
public class MessageSender {
    final static private Logger log = Logger.getLogger(MessageSender.class);

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @PostConstruct
    public void onConstruct() throws InterruptedException {
        log.info("Sending messages...");
        for (int i = 0; i < 100; ++i) {
            kafkaTemplate.send("mytopic", "this is a message");
            Thread.sleep(1000);
        }
        kafkaTemplate.flush(); // NOTE: no changes if I move this call in the loop
        log.info("Done sending messages");
    }
}

消费

KafkaConsumerConfiguration.java

@Configuration
@EnableKafka
public class KafkaConsumerConfiguration {

    @Bean
    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());

        return factory;
    }

    @Bean
    public ConsumerFactory<String, String> consumerFactory() {
        return new DefaultKafkaConsumerFactory<>(consumerConfigs());
    }

    @Bean
    public Map<String, Object> consumerConfigs() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:32768");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-service");
        return props;
    }
}

MyMessageListener.java

@Service
public class MyMessageListener {
    final static private Logger log = Logger.getLogger(MyMessageListener.class);

    @PostConstruct
    public void onConstruct() {
        log.info("Message listener started");
    }

    @KafkaListener(topics = "mytopic")
    public void onMessageReceived(String message) {
        log.info("Got message: "+ message);
    }
}

以下是应用程序生成的日志,供参考:https://pastebin.com/BY783jiL。如您所见,未创建消费者bean(否则会出现块ConsumerConfig values: ...

以下是我尝试过的一些没有成功的事情:

  • 将生产者和消费者配置放在同一个配置类中
  • 更改KafkaConsumerConfiguration中bean的名称(并在containerFactory = "myBeanName"方法上添加注释属性MyMessageListener.onMessageReceived
  • 将班级KafkaConsumerConfiguration的名称更改为其他内容
  • 在我的@Bean中添加与kafka无关的KafkaConsumerConfiguration以查看是否会创建(确实如此)

版本:Spring Boot 1.5.9,Spring-Kafka:1.1.7。

我现在已经把头发弄了几个小时,任何帮助都会受到赞赏。

谢谢!

2 个答案:

答案 0 :(得分:2)

kafkaTemplate.send("mytopic", "this is a message");

您永远不应该开始使用@PostConstruct方法与外部服务进行交互 - 您需要等待应用程序构建之前。

实施SmartLifecyle,为true返回isAutoStartup并将该代码移至start()

或者实施ApplicationListener<ConstextRefreshedEvent>并在收到活动时进行发送。

无论哪种方式都可以确保应用程序准备就绪。

答案 1 :(得分:0)

刚刚发现了这个问题。 MessageSender.onConstruct实际上需要花费大量时间来执行(100秒),同时它会阻止Spring创建其他bean。