我正在使用Spring和Spring Kafka编写一个小型PoC。我的目标是同时拥有一个制作人和一个消费者,从这个主题写作(再读)。
我遇到了一个奇怪的情况:
以下是我的代码 - 它与文档示例非常相似。更确切地说,问题来自于 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: ...
。
以下是我尝试过的一些没有成功的事情:
containerFactory = "myBeanName"
方法上添加注释属性MyMessageListener.onMessageReceived
)KafkaConsumerConfiguration
的名称更改为其他内容@Bean
中添加与kafka无关的KafkaConsumerConfiguration
以查看是否会创建(确实如此)版本:Spring Boot 1.5.9,Spring-Kafka:1.1.7。
我现在已经把头发弄了几个小时,任何帮助都会受到赞赏。
谢谢!
答案 0 :(得分:2)
kafkaTemplate.send("mytopic", "this is a message");
您永远不应该开始使用@PostConstruct
方法与外部服务进行交互 - 您需要等待应用程序构建之前。
实施SmartLifecyle
,为true
返回isAutoStartup
并将该代码移至start()
。
或者实施ApplicationListener<ConstextRefreshedEvent>
并在收到活动时进行发送。
无论哪种方式都可以确保应用程序准备就绪。
答案 1 :(得分:0)
刚刚发现了这个问题。 MessageSender.onConstruct
实际上需要花费大量时间来执行(100秒),同时它会阻止Spring创建其他bean。