Spring引导rabbitMQ消费者因json转换失败而停止接收消息

时间:2017-02-07 14:48:41

标签: spring-boot rabbitmq spring-rabbit rabbitmq-exchange spring-rabbitmq

我使用的是spring-boot 1.4.2和rabbitMQ 1.6.5.RELEASE(不使用spring-boot-starter-rabbit)。我的项目中有多个微服务,大多数微服务都包含rabbitMQ消费者。项目中的一个将产生消息。即使消息在队列中可用,也很少有消费者停止从队列中获取消息。如果我重新启动该特定消费者,那么它将开始消费该消息。如果我重新部署任何消费者微服务,那么其他组件上的消费者很少会停止从队列中消费消息,但在队列中我可以看到消息。

查看日志后,我可以找到以下问题

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

所以杰克逊似乎无法将我的信息转换为json。所以在我的消费者而不是消耗我已经消耗的实际对象 org.springframework.amqp.core.Message 然后我手动将其转换为我的自定义对象。

我可以知道为什么spring-rabbit无法将消息转换为json?

请找到以下配置&消费者文件变更

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;
}

}

消费

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;
}

}

有没有办法避免手动转换?任何提示都有助于解决此问题

1 个答案:

答案 0 :(得分:0)

日志显示原因是BookFailConsumer.execute中的NullpointerException

所以这个消费者正在接收消息而不是BookConsumer,正如您所期望的那样。 您需要检查,为什么调用了BookFailConsumer并且那里出了什么问题。 (你没有发布BookFailConsumer的代码)