web-dev-qa-db-de.com

Spring Kafka - Wie setze ich den Offset mit einer Gruppen-ID auf den neuesten zurück?

Ich verwende derzeit Spring Integration Kafka, um Echtzeitstatistiken zu erstellen. Der Gruppenname veranlasst Kafka jedoch, alle vorherigen Werte zu durchsuchen, die der Listener nicht gelesen hat.

@Value("${kafka.consumer.group.id}")
private String consumerGroupId;

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

public Map<String, Object> getDefaultProperties() {
    Map<String, Object> properties = new HashMap<>();
    properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);

    properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroupId);

    properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
    return properties;
}

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {

    ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
    factory.setConsumerFactory(consumerFactory());
    return factory;
}

@Bean
public KafkaMessageListener listener() {
    return new KafkaMessageListener();
}

Ich möchte mit dem neuesten Offset beginnen und mich nicht von alten Werten stören lassen. Gibt es eine Möglichkeit, den Offset der Gruppe zurückzusetzen?

4
Bachrc

Weil ich kein Beispiel dafür gesehen habe, werde ich erklären, wie ich es hier gemacht habe.

Die Klasse Ihres @KafkaListener muss eine ConsumerSeekAware-Klasse implementieren, die es dem Listener ermöglicht, die Offset-Suche zu steuern, wenn Partitionen zugewiesen werden. (Quelle: https://docs.spring.io/spring-kafka/reference/htmlsingle/#seek )

public class KafkaMessageListener implements ConsumerSeekAware {
    @KafkaListener(topics = "your.topic")
    public void listen(byte[] payload) {
        // ...
    }

    @Override
    public void registerSeekCallback(ConsumerSeekCallback callback) {

    }

    @Override
    public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
        assignments.forEach((t, o) -> callback.seekToEnd(t.topic(), t.partition()));
    }

    @Override
    public void onIdleContainer(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {


    }
}

Bei einer Neuverteilung verwenden wir den angegebenen Rückruf, um den letzten Versatz für alle angegebenen Themen zu suchen. Danke an Artem Bilan ( https://stackoverflow.com/users/2756547/artem-bilan ), dass er mich zur Antwort geführt hat.

6
Bachrc

Nun, das klingt so, als müssten Sie sich um den auto.offset.reset des Verbrauchers sorgen. Aber was verwirrt mich, dass es sowieso standardmäßig latest ist:

auto.offset.reset   What to do when there is no initial offset in Kafka or if the current offset does not exist any more on the server (e.g. because that data has been deleted):

earliest: automatically reset the offset to the earliest offset
latest: automatically reset the offset to the latest offset
none: throw exception to the consumer if no previous offset is found for the consumer's group
anything else: throw exception to the consumer.

string  latest  [latest, earliest, none]    medium
1
Artem Bilan

Sie können eine ConsumerRebalanceListener für den kafka-Consumer festlegen, während Sie einige Themen abonnieren. Dabei können Sie den neuesten Versatz jeder Partition mit der KafkaConsumer.endOffsets()-Methode abrufen und diesen mit der KafkaConsumer.seek()-Methode wie folgt festlegen:

kafkaConsumer.subscribe(Collections.singletonList(topics),
    new ConsumerRebalanceListener() {
        @Override
        public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
            //do nothing
        }

        @Override
        public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
            //get and set the lastest offset for each partiton
            kafkaConsumer.endOffsets(partitions) 
                .forEach((partition, offset) -> kafkaConsumer.seek(partition, offset));
        }
    }
);
1
A.Chinese

sie können die Annotation partitionOffsets verwenden, um mit einem exakten Versatz zu beginnen. Beispiel:

@KafkaListener(id = "bar", topicPartitions =
    { @TopicPartition(topic = "topic1", partitions = { "0", "1" }),
      @TopicPartition(topic = "topic2", partitions = "0",
         partitionOffsets = @PartitionOffset(partition = "1", initialOffset = "100"))
    })public void listen(ConsumerRecord<?, ?> record) {
     }
0
link