所以基本上,我所看到的是我正在使用@KafkaListener来消费消息,并且故意不希望确认它们,因此使用者将忽略它们并在主题中侦听新消息。因此,如果应用程序重新启动,则使用者仅从最后提交的消息开始。因此,我想避免这种情况,希望消费者在不重新启动的情况下考虑未提交的消息。下面是我正在使用的配置和@kafkalistener。
ReceiverConfig.java
package com.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.kafka.listener.ContainerProperties;
import java.util.HashMap;
import java.util.Map;
@EnableKafka
@Configuration
public class ReceiverConfig {
@Value("${spring.kafka.bootstrap-servers}")
private String bootstrapServers;
@Value("${spring.kafka.group}")
private String group;
@Value("${spring.kafka.auto-offset-reset}")
private String offset;
@Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.GROUP_ID_CONFIG, group);
props.put(ConsumerConfig.CLIENT_ID_CONFIG, "CAS-CLIENT-001");
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");
props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, "3000");
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, offset);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); //TODO Remove if you want Kafka to automatically acknowledge
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1);
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30000);
return props;
}
@Bean
public ConsumerFactory<String, String> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
@Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(1);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE); //TODO Remove if you want Kafka to automatically acknowledge
return factory;
}
}
和ReceiverService
package com.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
@Service
public class ReceiverService {
private final Logger LOG = LoggerFactory.getLogger(ReceiverService.class);
@KafkaListener(topics = "${spring.kafka.topic}")
private synchronized void consumeKafkaQueue(@Payload String message, Acknowledgment acknowledgment) {
LOG.info("Received message from kafka queue: {}", message);
//TODO Do your code here...
//acknowledgment.acknowledge();
//Note if the "acknowledgment" parameter is left out Kafka will be acknowledged as soon as the method finishes.
}
}
答案 0 :(得分:0)
有什么价值?
@Value("${spring.kafka.auto-offset-reset}")
private String offset;
here,您可以看到所有使用者配置
当Kafka中没有初始偏移量或服务器上不再存在当前偏移量时(例如因为该数据已被删除),该怎么办:
最早:将偏移量自动重置为最早的偏移量
最新:自动将偏移量重置为最新的偏移量
无:如果未找到消费者组的先前偏移量,则向消费者抛出异常
其他:向消费者抛出异常。
您应该最早使用
答案 1 :(得分:0)
注册回调时,您必须实现ConsumerSeekAware
@Override
public void registerSeekCallback(ConsumerSeekCallback consumerSeekCallback) {
this.consumerSeekCallback = consumerSeekCallback;
}
此后,您可以调用就地搜索,而不是调用
if(...){
acknowledgment.acknowledge();
}else{
consumerSeekCallback.get().seek(consumerRecord.topic(), consumerRecord.partition(), consumerRecord.offset());
}
这是真的,因为spring使用内部批处理消息并将此代码用于检查确认:
protected void pollAndInvoke() {
if (!this.autoCommit && !this.isRecordAck) {
processCommits();
}
idleBetweenPollIfNecessary();
if (this.seeks.size() > 0) {
processSeeks();
}
...
答案 2 :(得分:0)
即使在应用程序重新启动或以某种方式触发重新平衡之前,消费者也不会读取过去的几条消息,即使这些消息未提交。请参阅以下摘自 Kafka 权威指南。
重新平衡后,所有消费者将从上次提交的偏移量开始消费。
您可以通过连接几个消费者并删除一个来强制重新平衡。一旦超过默认的 10 秒限制,将触发重新平衡,您可以看到剩余的消费者将阅读这些消息。
而且这里没有 auto.offset.reset
属性的关系。
您只需按照此处的自述文件即可创建 Kafka 集群。