web-dev-qa-db-fra.com

Spring Kafka - Comment réinitialiser le décalage au plus tard avec un ID de groupe?

J'utilise actuellement Spring Integration Kafka pour établir des statistiques en temps réel. Cependant, le nom du groupe fait rechercher par Kafka toutes les valeurs précédentes que l'auditeur n'a pas lues.

@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();
}

Je voudrais commencer avec le dernier décalage et ne pas être dérangé par les anciennes valeurs. Existe-t-il une possibilité de réinitialiser le décalage du groupe?

4
Bachrc

Parce que je n'ai vu aucun exemple de cela, je vais expliquer comment je l'ai fait ici.

La classe de votre @KafkaListener doit implémenter une classe ConsumerSeekAware, qui permettra à l’auditeur de contrôler la recherche de décalage lorsque des partitions sont attribuées. (source: 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) {


    }
}

Ici, lors d'un rééquilibrage, nous utilisons le rappel donné pour rechercher le dernier décalage pour tous les sujets donnés. Merci à Artem Bilan ( https://stackoverflow.com/users/2756547/artem-bilan ) de m'avoir guidé vers la réponse.

6
Bachrc

On dirait que vous devez vous soucier du auto.offset.reset du consommateur. Mais ce qui me rend confus que c’est quand même latest:

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

Vous pouvez définir une variable ConsumerRebalanceListener pour le consommateur kafka lors de votre abonnement à certaines rubriques, dans laquelle vous pouvez obtenir le dernier décalage de chaque partition par la méthode KafkaConsumer.endOffsets() et définir ceci en consommateur par la méthode KafkaConsumer.seek(), comme ceci:

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

vous pouvez utiliser l'annotation partitionOffsets pour commencer avec le décalage exact, par exemple:

@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