web-dev-qa-db-fra.com

Comment utiliser la méthode Acknowledgement.acknowledge () de Spring Kafka pour la validation manuelle

J'utilise Spring Kafka la première fois et je ne suis pas en mesure d'utiliser la méthode Acknowledgement.acknowledge () pour la validation manuelle dans mon code consommateur comme mentionné ici https: //docs.spring .io/spring-kafka/reference/html/_reference.html # committing-offsets . Le mien est une application de démarrage de printemps. Si je n'utilise pas le processus de validation manuelle, mon code fonctionne correctement. Mais lorsque j'utilise la reconnaissance. reconnait () pour la validation manuelle, il affiche une erreur liée au bean. Si je n'utilise pas correctement la validation manuelle, veuillez me suggérer la bonne façon de le faire.

Message d'erreur:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field ack in Receiver required a bean of type 'org.springframework.kafka.support.Acknowledgment' that could not be found.


Action:

Consider defining a bean of type 'org.springframework.kafka.support.Acknowledgment' in your configuration.

J'ai googlé cette erreur, j'ai trouvé que je devais ajouter @Component, mais c'est déjà là dans mon code de consommation.

Mon code de consommation ressemble à ceci: Receiver.Java

import Java.util.concurrent.CountDownLatch;
import org.Apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

    @Autowired
    public Acknowledgment ack;

    private CountDownLatch latch = new CountDownLatch(1);

    @KafkaListener(topics = "${kafka.topic.TestTopic}")
    public void receive(ConsumerRecord<?, ?> consumerRecord){
            System.out.println(consumerRecord.value());
            latch.countDown();
            ack.acknowledge();
    }
}

Mon code producteur ressemble à ceci: Sender.Java

import Java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

@Component
public class Sender {

    @Autowired
    private KafkaTemplate<String, Map<String, Object>> kafkaTemplate;

    public void send(Map<String, Object> map){
            kafkaTemplate.send("TestTopic", map);

    }

}

EDIT 1:

Mon nouveau code de consommation ressemble à ceci: Receiver.Java

import Java.util.concurrent.CountDownLatch;
import org.Apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

    private CountDownLatch latch = new CountDownLatch(1);

    @KafkaListener(topics = "${kafka.topic.TestTopic}", containerFactory = "kafkaManualAckListenerContainerFactory")
    public void receive(ConsumerRecord<?, ?> consumerRecord, Acknowledgment ack){
            System.out.println(consumerRecord.value());
            latch.countDown();
            ack.acknowledge();
    }
}

J'ai également changé ma classe de configuration:

import Java.util.HashMap;
import Java.util.Map;

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.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;

@Configuration
@EnableKafka
public class ReceiverConfig {

    @Value("${kafka.bootstrap-servers}")
    private String bootstrapServers;

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

    @Bean
    public Map<String, Object> consumerConfigs() throws SendGridException {
            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, consumerGroupId);
            return props;

    }

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

    /*@Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(){
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());

        return factory;
    }*/

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaManualAckListenerContainerFactory(){
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        return factory;
    }

    @Bean
    public Receiver receiver() {
        return new Receiver();
    }
}

Après avoir ajouté containerFactory = "kafkaManualAckListenerContainerFactory" à ma méthode receive (), j'obtiens l'erreur ci-dessous.

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 1 of method kafkaListenerContainerFactory in org.springframework.boot.autoconfigure.kafka.KafkaAnnotationDrivenConfiguration required a bean of type 'org.springframework.kafka.core.ConsumerFactory' that could not be found.
    - Bean method 'kafkaConsumerFactory' in 'KafkaAutoConfiguration' not loaded because @ConditionalOnMissingBean (types: org.springframework.kafka.core.ConsumerFactory; SearchStrategy: all) found bean 'consumerFactory'


Action:

Consider revisiting the conditions above or defining a bean of type 'org.springframework.kafka.core.ConsumerFactory' in your configuration.
5
kumarhimanshu449

Vous devez vraiment suivre documentation :

Lors de l'utilisation du manuel AckMode, l'auditeur peut également disposer du Acknowledgment; cet exemple montre également comment utiliser une autre fabrique de conteneurs.

@KafkaListener(id = "baz", topics = "myTopic",
          containerFactory = "kafkaManualAckListenerContainerFactory")
public void listen(String data, Acknowledgment ack) {
    ...
    ack.acknowledge();
}

Il n'y a vraiment nulle part noté que Acknowledgment est un bean. Donc, changez votre signature de méthode receive()@KafkaListener de manière appropriée et supprimez ce @Autowired pour le bean Acknowledgment suspect - il n'existe tout simplement pas parce que cela l'objet fait partie (en-tête) de chaque message reçu.

9
Artem Bilan

Pour ceux qui recherchent toujours une solution à ces erreurs concernant l'acquittement manuel, vous n'avez pas besoin de spécifier containerFactory = "kafkaManualAckListenerContainerFactory", à la place, vous pouvez simplement ajouter:

factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);

à la configuration de votre récepteur juste avant de retourner l'objet d'usine.

Ensuite, vous avez également besoin de:

props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);

dans les accessoires de configuration des consommateurs.

Donc, à la fin, votre méthode d'écoute peut simplement ressembler à:

@KafkaListener(topics = "${spring.kafka.topic}")
    private void listen(@Payload String payload, Acknowledgment acknowledgment) {
        //Whatever code you want to do with the payload
        acknowledgement.acknowledge(); //or even pass the acknowledgment to a different method and acknowledge even later
    }
7
Zim8662