1

As one would expect its common to want to have different Consumers deserializing in different ways off topics in Kafka. There is a known problem with spring boot autoconfig. It seems that as soon as other factories are defined Spring Kafka or the autoconfig complains about not being able to find a suitable consumer factory anymore. Some have pointed out that one solution is to include a ConsumerFactory of type (Object, Object) in the config. But no one has shown the source code for this or clarified if it needs to be named in any particular way. Or if simply adding this Consumer to the config removes the need to turn off autoconfig. All that remains very unclear.

If you are not familiar with this issue please read https://github.com/spring-projects/spring-boot/issues/19221

Where it was just stated ok, define the ConsumerFactory and add it somewhere in your config. Can someone be a bit more precise about this please.

  1. Show exactly how to define the ConsumerFactory so that Spring boot autoconfig will not complain.
  2. Explain if turning off autoconfig is or is not needed?
  3. Explain if Consumer Factory needs to be named in any special way or not.
Roger Alkins
  • 125
  • 11
  • That's a legit ask..Take an upvote..How about creating your own producers/consumer(s) with custom serlialize, deserializers, and converters..etc. – z atef Jul 27 '20 at 04:04

1 Answers1

0

The simplest solution is to stick with Boot's auto-configuration and override the deserializer on the @KafkaListener itself...

@SpringBootApplication
public class So63108344Application {

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

    @KafkaListener(id = "so63108344-1", topics = "so63108344-1")
    public void listen1(String in) {
        System.out.println(in);
    }

    @KafkaListener(id = "so63108344-2", topics = "so63108344-2", properties =
            ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG +
                "=org.apache.kafka.common.serialization.ByteArrayDeserializer")
    public void listen2(byte[] in) {
        System.out.println(in);
    }

    @Bean
    public NewTopic topic1() {
        return TopicBuilder.name("so63108344-1").partitions(1).replicas(1).build();
    }

    @Bean
    public NewTopic topic2() {
        return TopicBuilder.name("so63108344-2").partitions(1).replicas(1).build();
    }

}

For more advanced container customization (or if you don't want to pollute the @KafkaListener, you can use a ContainerCustomizer...

@Component
class Customizer {
    
    public Customizer(ConcurrentKafkaListenerContainerFactory<?, ?> factory) {
        factory.setContainerCustomizer(container -> {
            if (container.getGroupId().equals("so63108344-2")) {
                container.getContainerProperties().setAckMode(AckMode.MANUAL_IMMEDIATE);
                container.getContainerProperties().getKafkaConsumerProperties()
                    .setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArrayDeserializer");
            }
        });
    }
    
}
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Also want to mention that I do have RequestReply templates in my KafkaConfig class – Roger Alkins Jul 29 '20 at 03:36
  • Hi Gary, will this still work if the producer is not sending type metadata information? Some of my topics are being sent messages by Kafka connectors which may not be including that metadata. – Roger Alkins Jul 30 '20 at 01:41
  • It's not clear what metadata you are talking about. – Gary Russell Jul 30 '20 at 13:23
  • Re 'metadata' - he meant the JSON type information headers https://stackoverflow.com/questions/63169373/how-to-include-type-metadata-for-deserialization-in-spring-kafka/63174367#63174367 – Gary Russell Aug 01 '20 at 15:04
  • Yes, I thats the deserialization I was talking about. I will accept the answer as soon as I successfully implement it! – Roger Alkins Aug 02 '20 at 10:16
  • So what I still need to know is what declaration do I use for the deserializer in the case where I am choosing the deserializer class using a method applied to the deserializer. Something like @KafkaListener(id = "so63108344-2", topics = "so63108344-2", properties = ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG + "=org.apache.kafka.common.serialization.MethodBasedDeserializer") public void listen2(bytes in) { System.out.println(in); } Where and how would the deserializer be created in the case where the type is determined by method? – Roger Alkins Aug 02 '20 at 16:26
  • Don't put code in comments; it is hard to read. You don't need a custom deserializer, you simply need to tell the `JsonDeserializer` the method name using its `JsonDeserializer.VALUE_TYPE_METHOD` consumer property. See [this answer](https://stackoverflow.com/questions/63098250/requestreplyfuturestring-string-listproduct-not-mapped-instead-it-mapped/63116682#63116682). – Gary Russell Aug 02 '20 at 17:08
  • Ok, great. Just one last clarification. How does value type method effect request/reply templates like ReplyingKafkaTemplate – Roger Alkins Aug 04 '20 at 02:47
  • It makes no difference at all; the deserializer is invoked by the kafka consumer before Spring ever sees the result. – Gary Russell Aug 04 '20 at 13:51
  • Ok, trying this but getting exception Caused by: java.lang.IllegalStateException: No type information in headers and no default type provided. I did specify the static method and I did also give default type of Object and before that a warning 2020-08-05 17:46:32.886 WARN 4526 --- [ restartedMain] o.a.k.clients.consumer.ConsumerConfig : The configuration 'spring.json.value.type.method' was supplied but isn't a known config. – Roger Alkins Aug 05 '20 at 10:44
  • That doesn't make sense; the warning is from the kafka consumer because it doesn't recognize the property. Run it in a debugger to see what's happening; if the function is correctly configured we don't even look for headers; the configuration must be incorrect. If you can't figure it out, post a stripped down version of your app someplace and I'll take a look. See my latest edit to [this answer](https://stackoverflow.com/questions/63058608/spring-boot-rest-api-with-spring-kafka/63059105#63059105) for an example. – Gary Russell Aug 05 '20 at 14:08