web-dev-qa-db-fra.com

Spring Kafka bloc d'appels d'envoi asynchrone

J'utilise Spring-Kafka version 1.2.1 et, lorsque le serveur Kafka est en panne/inaccessible, le bloc d'appels d'envoi asynchrone pendant un certain temps. Il semble être le TCP timeout. Le code ressemble à ceci:

ListenableFuture<SendResult<K, V>> future = kafkaTemplate.send(topic, key, message);
future.addCallback(new ListenableFutureCallback<SendResult<K, V>>() {
    @Override
    public void onSuccess(SendResult<K, V> result) {
        ...
    }

    @Override
    public void onFailure(Throwable ex) {
        ...
    }
});

J'ai jeté un coup d'œil au code Spring-Kafka et il semble simplement passer la tâche à la bibliothèque cliente kafka, traduisant une interaction de rappel en une interaction d'objet future. la bibliothèque kafka client, le code devient plus complexe et je n'ai pas pris le temps de tout comprendre, mais je suppose qu'il peut faire des appels à distance (métadonnées, au moins?) dans le même fil.

En tant qu'utilisateur, je m'attendais à ce que les méthodes Spring-Kafka qui renvoient un futur reviennent immédiatement, même si le serveur distant kafka est inaccessible.

Toute confirmation si ma compréhension est fausse ou s'il s'agit d'un bug serait la bienvenue. J'ai fini par le rendre asynchrone de mon côté pour l'instant.

Un autre problème est que la documentation de Spring-Kafka indique, au début, qu'elle fournit des méthodes d'envoi synchrones et asynchrones. Je n'ai trouvé aucune méthode qui ne renvoie pas de futures, peut-être que la documentation doit être mise à jour.

Je suis heureux de fournir des détails supplémentaires si nécessaire. Merci.

14

En plus de l'annotation @EnableAsync sur une classe de configuration, l'annotation @Async doit être utilisée sur la méthode si vous appelez ce code.

http://www.baeldung.com/spring-async

Voici quelques fragments de code. Kafka configuration du producteur:

@EnableAsync
@Configuration
public class KafkaProducerConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(KafkaProducerConfig.class);

    @Value("${kafka.brokers}")
    private String servers;

    @Bean
    public Map<String, Object> producerConfigs() {
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        return props;
    }

    @Bean
    public ProducerFactory<String, GenericMessage> producerFactory(ObjectMapper objectMapper) {
        return new DefaultKafkaProducerFactory<>(producerConfigs(), new StringSerializer(), new JsonSerializer(objectMapper));
    }

    @Bean
    public KafkaTemplate<String, GenericMessage> kafkaTemplate(ObjectMapper objectMapper) {
        return new KafkaTemplate<String, GenericMessage>(producerFactory(objectMapper));
    }

    @Bean
    public Producer producer() {
        return new Producer();
    }
}

Et le producteur lui-même:

public class Producer {

    public static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);

    @Autowired
    private KafkaTemplate<String, GenericMessage> kafkaTemplate;

    @Async
    public void send(String topic, GenericMessage message) {
        ListenableFuture<SendResult<String, GenericMessage>> future = kafkaTemplate.send(topic, message);
        future.addCallback(new ListenableFutureCallback<SendResult<String, GenericMessage>>() {

            @Override
            public void onSuccess(final SendResult<String, GenericMessage> message) {
                LOGGER.info("sent message= " + message + " with offset= " + message.getRecordMetadata().offset());
            }

            @Override
            public void onFailure(final Throwable throwable) {
                LOGGER.error("unable to send message= " + message, throwable);
            }
        });
    }
}
11
Jorge C

Juste pour être sûr. L'annotation @EnableAsync est-elle appliquée? Je veux dire que cela pourrait être la clé pour spécifier le comportement de Future <>

2
Chad

La meilleure solution consiste à ajouter un écouteur de rappel au niveau du producteur.

@Bean
public KafkaTemplate<String, WebUserOperation> operationKafkaTemplate() {
    KafkaTemplate<String, WebUserOperation> kt = new KafkaTemplate<>(operationProducerFactory());
    kt.setProducerListener(new ProducerListener<String, WebUserOperation>() {

        @Override
        public void onSuccess(ProducerRecord<String, WebUserOperation> record, RecordMetadata recordMetadata) {
            System.out.println("### Callback :: " + recordMetadata.topic() + " ; partition = " 
                    + recordMetadata.partition()  +" with offset= " + recordMetadata.offset()
                    + " ; Timestamp : " + recordMetadata.timestamp() + " ; Message Size = " + recordMetadata.serializedValueSize());
        }

        @Override
        public void onError(ProducerRecord<String, WebUserOperation> producerRecord, Exception exception) {
            System.out.println("### Topic = " + producerRecord.topic() + " ; Message = " + producerRecord.value().getOperation());
            exception.printStackTrace();
        }
    });
    return kt;
}
0
Arpit Gupta