0

I am using spring-boot 1.4.2 and rabbitMQ 1.6.5.RELEASE(not using spring-boot-starter-rabbit). I have multiple micro-services in my project and most of the micro services contains rabbitMQ consumers. One among the project will produce message. Few consumers stop picking up the message from queue even though the message is available in the queue.If i restart that particular consumer then it will start consuming that message. If i re-deploy anyone of the consumer micro services then again few consumers on other components will stop consuming the message from queue but in the queue i can see the message.

After looking into the logs i can find the below issue

com.sample.global.bookStateUpdate.consumer.BookFailConsumer.execute(com.vodafone.smartlife.provisioning.common.model.Message) 
throws com.sample.global.bookStateUpdate.consumer.BookFailConsumer.exception.ConsumerException' threw exception 
org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:138)
org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:105) 
org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:778) 
org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:701) 
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:99)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:191)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1213)
org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:682) 
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1191)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1175)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1200(SimpleMessageListenerContainer.java:99)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1338)\n\tat java.lang.Thread.run(Thread.java:745)\n
Caused by: java.lang.NullPointerException: null 
com.sample.global.bookStateUpdate.consumer.BookFailConsumer.execute(BookFailConsumer.java:54)
sun.reflect.GeneratedMethodAccessor149.invoke(Unknown Source)\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
    org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:115)
org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:49)
org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:125)... 12 common frames omitted\n

So it seems Jackson could not convert my message into json. So in my consumer instead of consuming the actual object i have consumed org.springframework.amqp.core.Message then i manually converted that into my custom object which works.

May i know why spring-rabbit couldn't convert the message into json?

Please find the below configuration & consumer file change

package com.sample.global.rabbit.configuration;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.test.RabbitListenerTest;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
@EnableRabbit
@RabbitListenerTest(capture = true, spy = true)
public class RabbitMqConfiguration {

@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory());
    factory.setConcurrentConsumers(15);
    factory.setMaxConcurrentConsumers(15);
    factory.setMessageConverter(jsonMessageConverter());

    return factory;
}

@Bean
public CachingConnectionFactory connectionFactory()
{
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory("http://localhost:15672");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setRequestedHeartBeat(10);
    return connectionFactory;
}

@Bean
public MessageConverter jsonMessageConverter()
{
    return new Jackson2JsonMessageConverter();
}

@Bean(name = "MainTemplate")
@Primary
public RabbitTemplate rabbitTemplate()
{
    RabbitTemplate template = new RabbitTemplate(connectionFactory());
    template.setMessageConverter(jsonMessageConverter());
    return template;
}

}

Consumer

package com.sample.global.rabbit.consumer;

import org.springframework.amqp.core.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class BookFailConsumer {

private static final Logger logger = LoggerFactory.getLogger(BookFailConsumer.class);

private static final ObjectMapper objectMapper = new ObjectMapper();

@Autowired
@Qualifier(value = "MainTemplate")
private RabbitTemplate rabbitTemplate;

@RabbitListener(
        id = "book",
        bindings = @QueueBinding(
                value = @Queue(value = "sample.queue", durable = "true"),
                exchange = @Exchange(value = "sample.exchange", durable = "true", delayed = "true"),
                key = "sample.queue"
        )
)
public void handle(Message messageObject) {
    com.sample.global.rabbit.consumer.model.Message message = null;
    try {
        message = convertMessageBodyToTransaction(messageObject.getBody());
        //After manual JSON conversion it works fine.
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public Message convertMessageBodyToTransaction(byte[] messageBody) throws BadRequestException {
    com.sample.global.rabbit.consumer.model.Message message = null;
    String body = null;
    try {
        body = new String(messageBody, "UTF-8");
        logger.debug("Message body converted successfully to string: {}", body);
    } catch (UnsupportedEncodingException e) {
        throw new BadRequestException(e);
    }
    try {
        message = objectMapper.readValue(body, Message.class);
        logger.debug("Message body mapped successfully to Message object: {}", message.toString());
    } catch (Exception e){
        logger.error("Message conversion failed for the following message body: {}", body);
        throw new BadRequestException(e);
    }
    return message;
}

}

Is there any way to avoid manual conversion? Any hint would be useful to fix this issue

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
VelNaga
  • 3,593
  • 6
  • 48
  • 82
  • You need to show the complete stack trace - you should also fix the weird formatting. – Gary Russell Feb 07 '17 at 15:20
  • @GaryRussell I updated the full stack trace and formatted a bit.Your help should be appreciable – VelNaga Feb 07 '17 at 15:33
  • @GaryRussell Do you want me to post any more code ? – VelNaga Feb 07 '17 at 16:32
  • No; see the answer from Stefan - there's an NPE in your code. And you really should do a better job with formatting stack traces in questions like this. – Gary Russell Feb 07 '17 at 17:09
  • @GaryRussell please see my comments under Stefan answer. I clearly mentioned there is no NPE in my code also I can able to receive "org.springframework.amqp.core.Message" in my consumer which I can able to convert it into custom Json. – VelNaga Feb 07 '17 at 17:15
  • Is there anything am I missing in json converter ? – VelNaga Feb 07 '17 at 17:16
  • Well, that's what the current stack trace shows - why don't you repost a properly formatted complete stack trace so we can have a chance to help you? – Gary Russell Feb 07 '17 at 17:31
  • @GaryRussell thanks a lot let me check the logs again and re-post the exception. But do you have any guess I'm able to get the rabbit message but unable to get the custom message ? Meanwhile I'll search for any other stack traces in log file – VelNaga Feb 07 '17 at 17:47
  • I cannot guess without a stack trace. – Gary Russell Feb 07 '17 at 17:56

1 Answers1

0

The log shows the reason is a NullpointerException in BookFailConsumer.execute

So this consumer was receiving the message and not the BookConsumer, as you seem to expect. You need to check, why the BookFailConsumer was invoked and whats going wrong there. ( You did not post the code of BookFailConsumer )

  • Thank you for your comment. Actually it's a typo error while posting the code.I have only one consumer which is "BookFailConsumer". i do not have any consumer called "BookConsumer" also i have mentioned i can able to get "org.springframework.amqp.core.Message" inside consumer. – VelNaga Feb 07 '17 at 16:32