我正在使用Spring-Kafka编写我的第一个Kafka Consumer。考察了框架提供的不同选项,并且对此几乎没有疑问。有人可以在下面进行说明吗?
问题-1 :根据Spring-Kafka文档,有两种方法可以实现Kafka-Consumer; “您可以通过配置MessageListenerContainer并提供消息侦听器或使用@KafkaListener批注来接收消息”。有人可以告诉我何时应该选择一个选项而不是另一个吗?
问题-2 :我选择了KafkaListener方法来编写我的应用程序。为此,我需要初始化一个容器工厂实例,并且在容器工厂内部有控制并发的选项。只是想仔细检查一下我对并发的理解是否正确。
假设我有一个主题名称MyTopic,其中有4个分区。为了使用来自MyTopic的消息,我启动了我的应用程序的2个实例,并通过将并发设置为2来启动这些实例。因此,理想情况下,按照kafka分配策略,应将2个分区分配给consumer1,将另外2个分区分配给Consumer2 。由于并发设置为2,是否每个使用者都将启动2个线程,并并行使用主题中的数据?如果我们并行消费,我们还应该考虑什么。
问题3 -我选择了手动确认模式,而不是从外部管理偏移量(不将偏移量持久保存到任何数据库/文件系统中)。因此,我是否需要编写自定义代码来处理重新平衡,否则框架将自动对其进行管理?我认为没有,因为我只有在处理完所有记录后才确认。
问题-4 :此外,在“手动ACK”模式下,哪个Listener可以提高性能? BATCH消息侦听器或常规消息侦听器。我想如果我使用Normal Message侦听器,则在处理每条消息后将提交偏移量。
粘贴下面的代码以供参考。
批次确认使用者:
public void onMessage(List<ConsumerRecord<String, String>> records, Acknowledgment acknowledgment,
Consumer<?, ?> consumer) {
for (ConsumerRecord<String, String> record : records) {
System.out.println("Record : " + record.value());
// Process the message here..
listener.addOffset(record.topic(), record.partition(), record.offset());
}
acknowledgment.acknowledge();
}
初始化容器工厂:
@Bean
public ConsumerFactory<String, String> consumerFactory() {
return new DefaultKafkaConsumerFactory<String, String>(consumerConfigs());
}
@Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> configs = new HashMap<String, Object>();
configs.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootStrapServer);
configs.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
configs.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, enablAutoCommit);
configs.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPolInterval);
configs.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
configs.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId);
configs.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
configs.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
return configs;
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<String, String>();
// Not sure about the impact of this property, so going with 1
factory.setConcurrency(2);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(AckMode.MANUAL);
factory.getContainerProperties().setConsumerRebalanceListener(RebalanceListener.getInstance());
factory.setConsumerFactory(consumerFactory());
factory.getContainerProperties().setMessageListener(new BatchAckConsumer());
return factory;
}
答案 0 :(得分:1)
@KafkaListener
是消息驱动的“ POJO”,它添加了诸如有效负载转换,参数匹配等内容。如果实现MessageListener
,则只能获得原始的ConsumerRecord
来自卡夫卡。参见@KafkaListener Annotation。
是的,并发表示线程数;每个线程创建一个Consumer
;它们并行运行;在您的示例中,每个分区都有2个分区。
如果我们并行消费,我们还应该考虑什么。
您的侦听器必须是线程安全的(没有共享状态或任何此类状态都需要用锁保护。
您不清楚“处理重新平衡事件”的含义。当发生重新平衡时,框架将提交所有未决的偏移量。
没有什么区别;消息监听器批处理侦听器只是一个首选项。即使使用MANUAL ackmode的消息侦听器,在处理了所有轮询结果后,也会提交偏移量。在MANUAL_IMMEDIATE模式下,偏移量是一对一提交的。
答案 1 :(得分:1)
第一季度:
从文档
@KafkaListener注释用于将bean方法指定为 侦听器容器的侦听器。豆被包裹在一个 MessagingMessageListenerAdapter配置了各种功能,例如 作为转换器来转换数据(如果需要)以匹配方法 参数。
您可以通过使用SpEL在注释上配置大多数属性 “#{…。}或属性占位符($ {…。})。有关更多信息,请参见Javadoc。”
此方法对于简单的POJO侦听器可能很有用,并且您无需实现任何接口。您还可以使用注释以声明的方式收听任何主题和分区。您还可能返回返回的值,而对于MessageListener,则受接口签名的约束。
第二季度:
理想上是。如果您有多个主题可以使用,那么它将变得更加复杂。默认情况下,Kafka使用RangeAssignor,它具有自己的行为(您可以更改此行为-查看更多详细信息under)。
第三季度:
如果您的消费者死亡,将进行再平衡。如果您手动确认并且消费者在提交偏移量之前就去世了,则您无需执行任何操作,Kafka会处理。但是您最终可能会收到一些重复的消息(至少一次)
第四季度:
这取决于您所说的“性能”。如果您的意思是等待时间,那么尽快使用每条记录将是您的理想之选。如果要实现高吞吐量,则批次消耗会更有效。
我已经使用Spring kafka和各种监听器编写了一些示例-请查看this repo