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())
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.
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);
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