我正在尝试使用Spring Kafka 2.2中引入的新类型映射功能:
向下滚动到“映射类型”:
https://docs.spring.io/spring-kafka/reference/htmlsingle/#serdes
在生产者端,我注册了一个映射,如下所示:
senderProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
senderProps.put(JsonSerializer.TYPE_MAPPINGS, "foo:com.myfoo.Foo");
关于消费者,如下:
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
consumerProps.put(JsonDeSerializer.TYPE_MAPPINGS, "foo:com.yourfoo.Foo");
但是,在将事件发送到com.myfoo.Foo
类的生产者端的Kafka时,添加到记录中的classId标头是com.myfoo.Foo
而不是foo
。因此,在消费者方面,由于com.myfoo.Foo
未知,因此无法反序列化。
在spring-kafka中,我将问题缩小到此方法:
protected void addHeader(Headers headers, String headerName, Class<?> clazz) {
if (this.classIdMapping.containsKey(clazz)) {
headers.add(new RecordHeader(headerName, this.classIdMapping.get(clazz)));
}
else {
headers.add(new RecordHeader(headerName, clazz.getName().getBytes(StandardCharsets.UTF_8)));
}
}
在通过执行卡夫卡记录的序列化进行调试时,执行实际上转到else分支,而据我所知,它实际上应该转到if分支并添加foo
作为标题。而是将com.myfoo.Foo
添加到标题中。
罪魁祸首似乎与类加载器不匹配有关,但我不确定这是否是错误或我最后做的愚蠢。
基本上,classIdMapping
映射已正确填充了com.myfoo.Foo
作为键,而foo
的UTF-8 byte []表示形式是对应的值。
但是,在if chec期间,clazz
参数是一个类,它是由与classIdMapping
映射中存储的类不同的类装载器装载的,因此哈希码是不同的,并且转到else分支
这实际上是String-Kafka方面的错误,还是我配置错误?
谢谢
答案 0 :(得分:2)
这确实看起来像我们实施中的遗漏。请为Apache Kafka项目的Spring提出一个问题:https://github.com/spring-projects/spring-kafka。我们的想法是不使用Class<?>
进行映射,而是使用其完全合格的类名。
似乎您的应用程序是多类加载器,例如它是Web。然后,Apache Kafka Client将senderProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
加载到其ClassLoader中,而其他所有事情都由应用程序上下文ClassLoader完成。
作为一种解决方法,在解决此问题的同时,建议使用DefaultKafkaProducerFactory
配置及其setValueSerializer()
来填充JsonSerializer
bean参考。这样,您需要通过其setTypeMapper()
填充类映射,因此需要在setIdClassMapping()
上填充DefaultJackson2JavaTypeMapper
。