web-dev-qa-db-fra.com

Spring Kafka Producer n'envoie pas à Kafka 1.0.0 (Magic v1 ne prend pas en charge les en-têtes d'enregistrement)

J'utilise cette configuration docker-compos pour configurer Kafka localement: https://github.com/wurstmeister/kafka-docker/

docker-compose up fonctionne bien, la création de sujets via Shell fonctionne bien.

Maintenant, j'essaie de me connecter à Kafka via spring-kafka:2.1.0.RELEASE

Lors du démarrage de l'application Spring, la version correcte de Kafka est imprimée: 

o.a.kafka.common.utils.AppInfoParser     : Kafka version : 1.0.0
o.a.kafka.common.utils.AppInfoParser     : Kafka commitId : aaa7af6d4a11b29d

J'essaie d'envoyer un message comme celui-ci

kafkaTemplate.send("test-topic", UUID.randomUUID().toString(), "test");

L'envoi côté client échoue avec 

UnknownServerException: The server experienced an unexpected error when processing the request

Dans la console du serveur, le message Magic v1 ne prend pas en charge les en-têtes d'enregistrement

Error when handling request {replica_id=-1,max_wait_time=100,min_bytes=1,max_bytes=2147483647,topics=[{topic=test-topic,partitions=[{partition=0,fetch_offset=39,max_bytes=1048576}]}]} (kafka.server.KafkaApis)
Java.lang.IllegalArgumentException: Magic v1 does not support record headers

Googler suggère un conflit de version, mais la version semble correspondre (org.Apache.kafka:kafka-clients:1.0.0 est dans le classpath).

Des indices? Merci!

Edit: J'ai réduit la source du problème. L'envoi de chaînes ordinaires fonctionne, mais l'envoi de Json via JsonSerializer a pour résultat le problème indiqué. Voici le contenu de ma config de producteur: 

@Value("\${kafka.bootstrap-servers}")
lateinit var bootstrapServers: String

@Bean
fun producerConfigs(): Map<String, Any> =
        HashMap<String, Any>().apply {
            // list of Host:port pairs used for establishing the initial connections to the Kakfa cluster
            put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers)
            put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer::class.Java)
            put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer::class.Java)
        }

@Bean
fun producerFactory(): ProducerFactory<String, MyClass> =
        DefaultKafkaProducerFactory(producerConfigs())

@Bean
fun kafkaTemplate(): KafkaTemplate<String, MyClass> =
        KafkaTemplate(producerFactory())
7
DerM

Résolu. Le problème n'est ni le courtier, une partie du cache de menu fixe ni l'application Spring.

Le problème était un consommateur de console que j'ai utilisé en parallèle pour le débogage. C'était un "ancien" consommateur qui a commencé avec kafka-console-consumer.sh --topic=topic --zookeeper=...

En fait, il affiche un avertissement au démarrage: Using the ConsoleConsumer with old consumer is deprecated and will be removed in a future major release. Consider using the new consumer by passing [bootstrap-server] instead of [zookeeper].

Un "nouveau" consommateur avec l'option --bootstrap-server doit être utilisé (en particulier lors de l'utilisation de Kafka 1.0 avec JsonSerializer) . Remarque: l'utilisation d'un ancien consommateur ici peut effectivement affecter le producteur.

3
DerM

J'ai eu un problème similaire. Kafka ajoute des en-têtes par défaut si nous utilisons JsonSerializer ou JsonSerde pour les valeurs .Pour éviter ce problème, nous devons désactiver l'ajout des en-têtes d'informations.

si vous êtes d'accord avec la sérialisation JSON par défaut, utilisez ce qui suit (le point clé ici est ADD_TYPE_INFO_HEADERS): 

Map<String, Object> props = new HashMap<>(defaultSettings);
props.put(JsonSerializer.ADD_TYPE_INFO_HEADERS, false);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
ProducerFactory<String, Object> producerFactory = new DefaultKafkaProducerFactory<>(props);

mais si vous avez besoin d'une JsonSerializer personnalisée avec une ObjectMapper spécifique (comme avec PropertyNamingStrategy.SNAKE_CASE), vous devez désactiver l'ajout explicite des en-têtes d'informations sur JsonSerializer, car spring kafka ignore la propriété de DefaultKafkaProducerFactory, ADD_TYPE_INFO_HEADERS (car pour moi, c'est une mauvaise conception de spring kafka)

JsonSerializer<Object> valueSerializer = new JsonSerializer<>(customObjectMapper);
valueSerializer.setAddTypeInfo(false);
ProducerFactory<String, Object> producerFactory = new DefaultKafkaProducerFactory<>(props, Serdes.String().serializer(), valueSerializer);

ou si nous utilisons JsonSerde, alors:

Map<String, Object> jsonSerdeProperties = new HashMap<>();
jsonSerdeProperties.put(JsonSerializer.ADD_TYPE_INFO_HEADERS, false);
JsonSerde<T> jsonSerde = new JsonSerde<>(serdeClass);
jsonSerde.configure(jsonSerdeProperties, false);
10

Je viens de lancer un test contre cette image de docker sans aucun problème ...

$docker ps

CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                                                NAMES
f093b3f2475c        kafkadocker_kafka        "start-kafka.sh"         33 minutes ago      Up 2 minutes        0.0.0.0:32768->9092/tcp                              kafkadocker_kafka_1
319365849e48        wurstmeister/zookeeper   "/bin/sh -c '/usr/sb…"   33 minutes ago      Up 2 minutes        22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp   kafkadocker_zookeeper_1

.

@SpringBootApplication
public class So47953901Application {

    public static void main(String[] args) {
        SpringApplication.run(So47953901Application.class, args);
    }

    @Bean
    public ApplicationRunner runner(KafkaTemplate<Object, Object> template) {
        return args -> template.send("foo", "bar", "baz");
    }

    @KafkaListener(id = "foo", topics = "foo")
    public void listen(String in) {
        System.out.println(in);
    }

}

.

spring.kafka.bootstrap-servers=192.168.177.135:32768
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=false

.

2017-12-23 13:27:27.990  INFO 21305 --- [      foo-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions assigned: [foo-0]
baz

MODIFIER

Travaille toujours pour moi ...

spring.kafka.bootstrap-servers=192.168.177.135:32768
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=false
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer

.

2017-12-23 15:27:59.997  INFO 44079 --- [           main] o.a.k.clients.producer.ProducerConfig    : ProducerConfig values: 
    acks = 1
    ...
    value.serializer = class org.springframework.kafka.support.serializer.JsonSerializer

...

2017-12-23 15:28:00.071  INFO 44079 --- [      foo-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions assigned: [foo-0]
baz
1
Gary Russell