Spring Kafka Producer не отправляет Kafka 1.0.0 (Magic v1 не поддерживает заголовки записей)
я использую эту настройку docker-compose для настройки Кафки локально:https://github.com/wurstmeister/kafka-docker/
docker-compose up
отлично работает, создание тем через оболочку работает нормально.
теперь я пытаюсь подключиться к Кафке через spring-kafka:2.1.0.RELEASE
при запуске приложения Spring он печатает правильную версию Kafka:
o.a.kafka.common.utils.AppInfoParser : Kafka version : 1.0.0
o.a.kafka.common.utils.AppInfoParser : Kafka commitId : aaa7af6d4a11b29d
Я пытаюсь отправить сообщение, как это
kafkaTemplate.send("test-topic", UUID.randomUUID().toString(), "test");
отправка на стороне клиента не удается с
UnknownServerException: The server experienced an unexpected error when processing the request
в консоли сервера я получаю Magic v1 не поддерживает заголовки записей
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
Googling предлагает конфликт версий, но версия, похоже, подходит (org.apache.kafka:kafka-clients:1.0.0
находится в classpath).
какие-то зацепки? Спасибо!
изменить: Я сузил круг проблем. Отправка простых строк работает, но отправка Json через JsonSerializer приводит к данной проблеме. Вот содержание моего продюсера config:
@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())
3 ответов
у меня была аналогичная проблема. Кафка добавляет заголовки по умолчанию, если мы используем тег JsonSerializer
для значений.
Чтобы предотвратить эту проблему, нам нужно отключить добавление заголовков info.
если вы в порядке с сериализацией JSON по умолчанию, используйте следующее (ключевой момент здесь 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);
но если вам нужен пользовательский JsonSerializer
с конкретным ObjectMapper (например, с PropertyNamingStrategy.SNAKE_CASE
), вы должны отключить добавление заголовков информации явно на JsonSerializer
, как весной Кафка игнорирует DefaultKafkaProducerFactory
'ы собственность ADD_TYPE_INFO_HEADERS
(что касается меня, то это плохой дизайн весеннего Кафки)
JsonSerializer<Object> valueSerializer = new JsonSerializer<>(customObjectMapper);
valueSerializer.setAddTypeInfo(false);
ProducerFactory<String, Object> producerFactory = new DefaultKafkaProducerFactory<>(props, Serdes.String().serializer(), valueSerializer);
решена. Проблема не в брокере, некотором кэше docker или приложении Spring.
проблема заключалась в консольном потребителе, который я использовал параллельно для отладки. Это был" старый " потребитель, начатый с kafka-console-consumer.sh --topic=topic --zookeeper=...
он фактически печатает предупреждение при запуске: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].
"новый" потребитель с --bootstrap-server
опция должна использоваться (особенно при использовании Kafka 1.0 с JsonSerializer).
Примечание: использование старого потребителя здесь действительно может повлиять на производитель.
Я просто провел тест против этого изображения docker без проблем...
$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
редактировать
все еще работает для меня...
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