2

I am using spring-cloud-stream-kafka-binder-3.0.4 to consume the messages in batch, after consuming, converting the Message into object but getting the above exception.

Here is the code:

@StreamListener(ActivityChannel.ACTIVITY_INPUT_CHANNEL)
public void handleActivity(List<Message<Event>> messages,
    @Header(name = "deliveryAttempt", defaultValue = "1") int deliveryAttempt,
    @Header(KafkaHeaders.ACKNOWLEDGMENT) Acknowledgment acknowledgment)  
{
    try
    {
        log.info("Received activity message with message length {} attempt {}",
              messages.size(), deliveryAttempt);
        List<Event> eventList = messages.stream().map(Message::getPayload).collect(Collectors.toList());
        nodeConfigActivityBatchProcessor.processNodeConfigActivity(eventList);
        acknowledgment.acknowledge();
        log.debug("Processed activity message {} successfully!!", messages.size());
    } 
    catch (Exception e)
    { 
        throw e;
    }
}

Configuration:

spring.cloud.stream.bindings.activity-input-channel.destination=TOPIC.FEED.NAME
spring.cloud.stream.bindings.activity-input-channel.contentType=application/json
spring.cloud.stream.bindings.activity-input-channel.consumer.batch-mode=true
spring.cloud.stream.bindings.activity-input-channel.consumer.max-attempts=1
spring.cloud.stream.kafka.bindings.activity-input-channel.consumer.auto-commit-offset=false
spring.cloud.stream.kafka.bindings.activity-input-channel.consumer.reset-offsets=true
spring.cloud.stream.kafka.bindings.activity-input-channel.consumer.start-offset=latest
spring.kafka.consumer.max-poll-records=5
spring.kafka.consumer.fetch-max-wait=60000
spring.kafka.consumer.fetch-min-size=500

I get the above error at this line List<Event> eventList = messages.stream().map(Message::getPayload).collect(Collectors.toList()); at .collect(Collectors.toList()). I am not able to figure out why??

if I check Message<Event> eventMessage = messages.get(0) getting the same exception(messages is the list of Message variable).

if batch mode if false then it only consume a single message handleActivity(Message message), then it works fine, no exception.

Is there any deserializer needs to be added when using batch mode???

APK
  • 155
  • 1
  • 15
  • that message means you are trying to cast an instance of B to an instance of Message, but there is no IS-A relationship between the two, which is why it fails. – Stultuske Sep 17 '21 at 09:16
  • I publish messages to kafka producer, and this listener consume in batch of 5, but after that I am not able to get the first element from the list it received (Which is of Message format). Same way, if batch mode if false then it only consume a single message handleActivity(Message message), then it works fine. what is B type , I don't understand – APK Sep 17 '21 at 09:22
  • I don't know what B is neither, but that is the type mentioned in your error message – Stultuske Sep 17 '21 at 09:26

2 Answers2

1

I managed to solve this Exception, by adding a Deserialiser.

So Below is my batch Listener, Instead of consuming List<Message<Event>> messages as mentioned in the question, consuming List<Event> messages.

@StreamListener(ActivityChannel.ACTIVITY_INPUT_CHANNEL)
 public void handleActivity(List<Event> messages,@Header(KafkaHeaders.ACKNOWLEDGMENT) Acknowledgment acknowledgment)  
 {
  try
  {
    log.info("Received activity message with message length {} attempt 
    {}",messages.size(), deliveryAttempt);        
   nodeConfigActivityBatchProcessor.processNodeConfigActivity(messages);
    acknowledgment.acknowledge();
    log.debug("Processed activity message {} successfully!!", 
   messages.size());
 } 
 catch (Exception e)
  { 
      throw e;
  }
}

Added the below Deserialiser

public class EventDeserializer extends JsonDeserializer<Event> {
}

Added below value.deserializer property to properties file.

spring.cloud.stream.kafka.bindings.input-channel.consumer.configuration.value.deserializer=com.sample.messaging.util.EventDeserializer

I got list of events in my batch listener.

APK
  • 155
  • 1
  • 15
1

It is not mentioned anywhere unfortunately :-(, but to wrap input events into org.springframework.messaging.Message (to get record keys for example), here is the correct signature when doing batch processing:

@Bean
public Consumer<Message<List<String>>> sink() {
    return messages -> {
        List<?> keys = messages.getHeaders().get(KafkaHeaders.RECEIVED_MESSAGE_KEY, List.class);
        log.info("headers {}", messages.getHeaders());
        keys.forEach(k -> log.info("key {}", k));
        messages.getPayload().forEach(string -> log.info("processing {}", string));
    };
}

You notice that is the signed the other way around: Message<List<?>> rather than List<Message<?>>!