我正在使用以下示例来使用Spring Kafka使用者读取消息。我的用例要求每次产生一条消息时,侦听器都会从头开始每次读取。
@KafkaListener(
id = "grouplistener",
topicPartitions = {
@TopicPartition(
topic = "mycompactedtopic", partitionOffsets = @PartitionOffset(partition = "0", initialOffset = "0")
)
}
)
public void onReceiving(
String payload, @Header(KafkaHeaders.OFFSET) Integer offset,
@Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition,
@Header(KafkaHeaders.RECEIVED_TOPIC) String topic
) {
log.info(
"Processing topic = {}, partition = {}, offset = {}, payload= {}",
topic, partition, offset, payload
);
}
我似乎只能从应用程序启动时就开始读取它,然后通常只消耗向前发送的消息。
是否有一种方法可以迫使它每次尝试开始?
答案 0 :(得分:0)
使用具有1个分区的压缩主题来保存配置列表。然后需要由其他端点调用,并且应该显示配置的完整唯一列表
您应该执行此操作的方法是使用Kafka Streams和KTable,并在REST层后面设置interactive queries。不是需要倒带以获取系统的最新状态的标准使用者。
Kafka Connect框架中已有一个配置示例,其中有一个配置主题,您只能访问GET /connectors/name/config
的最新值,并且只有重新启动它或扩展到更多实例时,它将再次消耗所有消息。架构注册表也是这样的一个示例,它在_schemas
主题中存储了所有架构的内部Hashmap,并且具有用于读取,插入,删除
基本上,当您为给定密钥获得新配置时,可以用一个全新的密钥“替换”给定密钥的旧值,也可以在新数据中“合并”旧值。某种方式。
答案 1 :(得分:0)
我认为您应该尝试编写ConsumerSeekAwareListener,并在每次阅读消息时都尝试将偏移量设置为0。听起来像是疯狂的解决方法,但这可能会有所帮助。希望这会对您有所帮助:-)
class Listener implements ConsumerSeekAware {
private final ThreadLocal<ConsumerSeekCallback> seekCallBack = new ThreadLocal<>();
----Override all methods that are needed----
@KafkaListener(...)
public void listen(@Payload String message) {
this.seekCallBack.get().seek(topic, partition, 0);
}
}
}
答案 2 :(得分:0)
@ Nimo1981这是使用纯Java的实现。我不确定它是否满足您的需求。因此,基本上,我将偏移量设为0,(意味着,即使我从Kafka主题阅读,我也会回到开始时的偏移量。)我不确定您是否考虑过此实现,但是请告诉我是您要寻找的东西
忽略CommitCountObj。您的那不需要。 因此,默认情况下, offsetMap 将具有下一个这样的偏移记录
offsetMap.put(new TopicPartition(record.topic(),record.partition()), 新的OffsetAndMetadata(record.offset()+ 1,“一些提交成功消息”));
但是对于您的用例,我进行了某种修改,在不重新启动使用者时它可以很好地工作
offsetMap.put(new TopicPartition(record.topic(),record.partition()), 新的OffsetAndMetadata(0,“未完成提交”));
public class KafkaConsumerClass {
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(KafkaConsumerClass.class);
private CommitCountClass commitCountobj = new CommitCountClass();
public Consumer<String, List<FeedBackConsumerClass>> createConsumer() {
Map<String, Object> consumerProps = new HashMap<String, Object>();
consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:7070,localhost:7072");
consumerProps.put(ConsumerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG, 50000);
consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "first_group-client1");
// consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "first_group");
// consumerProps.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, KafkaConsumerInterceptor.class);
consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
consumerProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 15000);
consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
consumerProps.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1500);
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
return new KafkaConsumer<String, List<FeedBackConsumerClass>>(consumerProps);
}
public void consumeRecord() {
log.info("Coming inside consumer consumer");
ArrayList<String> topicList = new ArrayList<String>();
topicList.add("topic1");
commitCountobj.setCount(0);
Consumer<String, List<FeedBackConsumerClass>> kafkaConsumer = createConsumer();
kafkaConsumer.subscribe(topicList);
log.info("after subscribing");
Map<TopicPartition, OffsetAndMetadata> offsetMap = new HashMap<>();
while (true) {
ConsumerRecords<String, List<FeedBackConsumerClass>> recordList = kafkaConsumer.poll(Long.MAX_VALUE);
// kafkaConsumer.seekToBeginning(kafkaConsumer.assignment());
log.info("Inside while loop:" + recordList);
if (!recordList.isEmpty()) {
recordList.forEach(record -> {
int i = 0;
System.out.println(record.toString());
// we can make the call to the API here
// call the db here or any API and process the record
// then call the code to commit
// since the commit is switched off, it becomes a developers responsibility to do the auto commit
offsetMap.put(new TopicPartition(record.topic(), record.partition()),
new OffsetAndMetadata(0, "no metadata/offset commited"));
// here we are incrementing the offsetMap so that we are making sure we are storing the
// next set of offsets in the map
if (commitCountobj.getCount() % 1000 == 0) {
kafkaConsumer.commitAsync(offsetMap, new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets,
Exception exception) {
// TODO Auto-generated method stub
if (exception != null) {
// retry it now with a sync
// possibility of error occuring here as well
// so capture the exception and exit the consumer gracefully
kafkaConsumer.commitSync();
log.error(exception.getMessage());
}
}
});
}
commitCountobj.setCount(i++);
});
}
}
}
}
答案 3 :(得分:0)
这是我将如何实施。您需要实现ConsumerSeekAware
接口并在onPartitionsAssigned
方法上进行一些实现。如果在重新启动应用程序时发送环境变量,也可以按需使seekToBegining。我还没有实现!
@Service
@EnableKafka
public class Service implements ConsumerSeekAware {
@KafkaListener(topics = "${topicName}", groupId = "${groupId}")
public void listen(@Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition,
@Header(KafkaHeaders.RECEIVED_TIMESTAMP) long ts,
@Payload List<String> messageBatch
) {
//do a bunch of stuff
}
@Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
String topic= Optional.ofNullable(System.getProperty(TOPIC_NAME)).orElseThrow(()->new RuntimeException("topicName needs to be set"));
assignments.keySet().stream().filter(partition->topic.equals(partition.topic()))
.forEach(partition -> callback.seekToBeginning(topic, partition.partition()));
}
@Override
public void onIdleContainer(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {}
@Override
public void registerSeekCallback(ConsumerSeekCallback callback) {}
}
答案 4 :(得分:0)
@KafkaListener(topicPartitions
= @TopicPartition(topic = "test", partitionOffsets = {
@PartitionOffset(partition = "0", initialOffset = "0")}),groupId = "foo",
containerFactory = "kafkaListenerContainerFactory")
public void listenAllMsg(@Payload String message,@Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
System.out.println(" all msg Received Messasge in group 'foo': " + message+"RECEIVED_PARTITION_ID - "+partition);
}
在kafka 2.3.1中