【问题标题】:MessageConversionException: Failed to convert message contentMessageConversionException:无法转换消息内容
【发布时间】:2016-10-25 13:03:35
【问题描述】:

我有一个使用 Spring AMQP 的 SpringBoot 应用程序。目前,我正在按照官方 Spring 文档示例的指示使用 JavaConfig 类来实现它的接收方。但是,我收到 MessageConversionException 并且消息甚至没有到达我的 ClientHandler 类来处理消息。以下是该事件的控制台日志:

Caused by: org.springframework.amqp.support.converter.MessageConversionException: Failed to      convert Message content
    at org.springframework.amqp.support.converter.JsonMessageConverter.fromMessage(JsonMessageConverter.java:105) ~[spring-amqp-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:185) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:243) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    ... 10 common frames omitted
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of byte out of START_OBJECT token
 at [Source: java.io.StringReader@33b39883; line: 1, column: 1]
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.std.StdDeserializer._parseByte(StdDeserializer.java:214) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.std.StdDeserializer$ByteDeserializer.deserialize(StdDeserializer.java:749) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.std.StdDeserializer$ByteDeserializer.deserialize(StdDeserializer.java:736) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1877) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.springframework.amqp.support.converter.JsonMessageConverter.convertBytesToObject(JsonMessageConverter.java:131) ~[spring-amqp-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.support.converter.JsonMessageConverter.fromMessage(JsonMessageConverter.java:100) ~[spring-amqp-1.4.6.RELEASE.jar:na]
    ... 13 common frames omitted
2016-10-25 12:51:42,020 WARN  t:[SimpleAsyncTaskExecutor-1] ConditionalRejectingErrorHandler: Fatal message conversion error; message rejected; it will be dropped or routed to a dead letter exchange, if so configured: (Body:'{"messageDTOList":[{"createdDate":1477396301061,"message":"{\"mac\":-99,\"device\":444,\"ifindex\":55,\"ip\":668,\"arpdevice\":99,\"aprifindex\":\"\",\"vlanindex\":111,\"enddevice\":133}","crudOperation":"UPDATE"}],"senderServerId":"5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b","messageId":"5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b20","rebasoftServerType":"AC","taskType":"MAC"}'MessageProperties [headers={}, timestamp=null, messageId=null, userId=null, appId=null, clusterId=null, type=null, correlationId=[53, 98, 51, 54, 97, 55, 97, 49, 45, 99, 49, 55, 100, 45, 52, 55, 49, 102, 45, 98, 49, 54, 101, 45, 101, 56, 101, 48, 98, 102, 52, 97, 102, 98, 53, 98, 50, 48], replyTo=null, contentType=application/json, contentEncoding=null, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=X, receivedRoutingKey=data.core.macs, deliveryTag=2, messageCount=0])
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:864) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:759) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:679) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:83) [spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:170) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1253) [spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1021) [spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1005) [spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:83) [spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1115) [spring-rabbit-1.4.6.RELEASE.jar:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_51]
Caused by: org.springframework.amqp.support.converter.MessageConversionException: Failed to convert Message content
    at org.springframework.amqp.support.converter.JsonMessageConverter.fromMessage(JsonMessageConverter.java:105) ~[spring-amqp-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:185) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:243) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756) ~[spring-rabbit-1.4.6.RELEASE.jar:na]
    ... 10 common frames omitted
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of byte out of START_OBJECT token
 at [Source: java.io.StringReader@33b39883; line: 1, column: 1]
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:219) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.std.StdDeserializer._parseByte(StdDeserializer.java:214) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.std.StdDeserializer$ByteDeserializer.deserialize(StdDeserializer.java:749) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.deser.std.StdDeserializer$ByteDeserializer.deserialize(StdDeserializer.java:736) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1877) ~[jackson-mapper-asl-1.9.13.jar:1.9.13]
    at org.springframework.amqp.support.converter.JsonMessageConverter.convertBytesToObject(JsonMessageConverter.java:131) ~[spring-amqp-1.4.6.RELEASE.jar:na]
    at org.springframework.amqp.support.converter.JsonMessageConverter.fromMessage(JsonMessageConverter.java:100) ~[spring-amqp-1.4.6.RELEASE.jar:na]
    ... 13 common frames omitted

我的客户类别如下:

AbstractEMCRabbitConfiguration.java

@Configuration
public abstract class AbstractEMCRabbitConfiguration {

//@Value("${emc.rabbit.exchange.core}")
protected static String CORE_DATA_EXCHANGE_NAME = "X";

@Value("${emc.rabbit.queue.core.macs}")
protected static String MAC_REQUEST_QUEUE_NAME = "data.core.macs";

protected static String MAC_REQUEST_ROUTING_KEY = MAC_REQUEST_QUEUE_NAME;

//@Value("${emc.rabbit.hostname}")
protected String hostname;

//@Value("${emc.rabbit.username}")
protected String username;

//@Value("${emc.rabbit.password}")
protected String password;

//@Value("${emc.rabbit.port:5672}")
protected Integer port = 5672;

protected abstract void configureRabbitTemplate(RabbitTemplate template);

@Bean
public ConnectionFactory connectionFactory(){
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
    connectionFactory.setUsername("test");
    connectionFactory.setPassword("test");
    connectionFactory.setPort(port);
    connectionFactory.setRequestedHeartBeat(60);
    return connectionFactory;
}

@Bean
public RabbitTemplate rabbitTemplate(){
    RabbitTemplate template = new RabbitTemplate(connectionFactory());
    template.setMessageConverter(jsonMessageConverter());
    configureRabbitTemplate(template);
    return template;
}

@Bean
public MessageConverter jsonMessageConverter() {
    JsonMessageConverter jsonMessageConverter = new JsonMessageConverter();
    jsonMessageConverter.setClassMapper(defaultClassMapper());
    return jsonMessageConverter;
}

@Bean
public DefaultClassMapper defaultClassMapper() {
    DefaultClassMapper defaultClassMapper = new DefaultClassMapper();
    defaultClassMapper.setDefaultType(byte.class);
    return defaultClassMapper;
}

@Bean
public DirectExchange coreDataExchange() { return new DirectExchange(CORE_DATA_EXCHANGE_NAME); }

@Bean
public AmqpAdmin amqpAdmin() {
    RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory());
    return rabbitAdmin;
}
}

RabbitClientConfiguration.java

@Configuration
@Import({JacksonConfiguration.class, MessagingConfiguration.class})
public class RabbitClientConfiguration extends AbstractEMCRabbitConfiguration {

@Inject
private JacksonConfiguration jacksonConfiguration;

@Inject
private MessagingConfiguration messagingConfiguration;

//@Value("${data.core.pattern}")
//private String coreDataRoutingKey;

//@Inject
//private ClientHandler clientHandler;

@Override
public void configureRabbitTemplate(RabbitTemplate rabbitTemplate) {
    rabbitTemplate.setRoutingKey("data.core.macs");
}

@Bean
public SimpleMessageListenerContainer messageListenerContainer(){
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(connectionFactory());
    container.setQueues(macsDataQueue());
    container.setMessageListener(messageListenerAdapter());
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    return container;
}

@Bean
MessageListenerAdapter messageListenerAdapter(){
    return new MessageListenerAdapter(clientHandler(jacksonConfiguration.objectMapper(),messagingConfiguration.macMessageHandler(messagingConfiguration.messagingFactory())),jsonMessageConverter());
    //return new MessageListenerAdapter(clientHandler,"receiveMessage");
}

@Bean
ClientHandler clientHandler(ObjectMapper objectMapper, IMessageHandler macMessageHandler){
    ClientHandler clientHandler = new ClientHandler();
    clientHandler.setObjectMapper(objectMapper);
    clientHandler.setMacMessageHandler(macMessageHandler);
    return clientHandler;
}

@Bean
public Queue macsDataQueue(){
    return rabbitAdmin().declareQueue();
}

@Bean
public Binding macsDataBinding(){
    return BindingBuilder.bind(macsDataQueue()).to(coreDataExchange()).with("data.core.macs");
}

@Bean
public AmqpAdmin rabbitAdmin() { return new RabbitAdmin(connectionFactory());}

}

从代理收到的典型消息如下所示:

(正文:'{"messageDTOList":[{"createdDate":1477396301061,"message":"{\"mac\":-99,\"device\":444,\"ifindex\":55 ,\"ip\":668,\"arpdevice\":99,\"aprifindex\":\"\",\"vlanindex\":111,\"enddevice\":133}","crudOperation": "UPDATE"}],"senderServerId":"5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b","messageId":"5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b20","re​​basoftServerType":"AC","taskType": "MAC"}'MessageProperties [headers={},timestamp=null,messageId=null,userId=null,appId=null,clusterId=null,type=null,correlationId=[53、98、51、54、97、55 , 97, 49, 45, 99, 49, 55, 100, 45, 52, 55, 49, 102, 45, 98, 49, 54, 101, 45, 101, 56, 101, 48, 98, 102, 52 , 97, 102, 98, 53, 98, 50, 48],replyTo=null,contentType=application/json,contentEncoding=null,contentLength=0,deliveryMode=PERSISTENT,expiration=null,priority=0,redelivered=false, receivedExchange=X, receivedRoutingKey=data.core.macs, deliveryTag=2, messageCount=0])

注意:内容类型为'application/json'

如果有任何帮助,我将不胜感激。

【问题讨论】:

    标签: spring rabbitmq spring-integration spring-amqp spring-rabbit


    【解决方案1】:

    原因:org.codehaus.jackson.map.JsonMappingException:无法反序列化 START_OBJECT 令牌中的字节实例

    看起来像垃圾消息 - 你确定它是由带有 json 转换器的兔子模板创建的吗?

    您可以编辑问题并发布消息内容(以及 content_type 属性和其他标题)吗? (您可以使用管理 UI 来检查消息)。

    编辑

    使用此应用解码您的消息没有问题...

    @SpringBootApplication
    public class So40240771Application {
    
        public static void main(String[] args) throws Exception {
            ConfigurableApplicationContext context = SpringApplication.run(So40240771Application.class, args);
            RabbitTemplate template = context.getBean(RabbitTemplate.class);
            String body = "{"
                    + "\"messageDTOList\":"
                    + "[{\"createdDate\":1477396301061,"
                    +   "\"message\":\"{\\\"mac\\\":-99,\\\"device\\\":444,\\\"ifindex\\\":55,\\\"ip\\\":668,\\\"arpdevice\\\":99,\\\"aprifindex\\\":\\\"\\\",\\\"vlanindex\\\":111,\\\"enddevice\\\":133}\","
                    +   "\"crudOperation\":\"UPDATE\"}],"
                    + "\"senderServerId\":\"5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b\","
                    + "\"messageId\":\"5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b20\","
                    + "\"rebasoftServerType\":\"AC\","
                    + "\"taskType\":\"MAC\""
                    + "}";
            MessageProperties properties = new MessageProperties();
            properties.setContentType("application/json");
            template.send("foo", MessageBuilder.withBody(body.getBytes()).andProperties(properties).build());
            Thread.sleep(6000);
            context.close();
        }
    
        @Bean
        public Jackson2JsonMessageConverter converter() {
            Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
            return converter;
        }
    
        @Bean
        public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) {
            SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
            container.setMessageListener(new MessageListenerAdapter(new Object() {
                void handleMessage(Object in) {
                    System.out.println(in.getClass() + "\n" + in);
                }
            }, converter()));
            container.setQueueNames("foo");
            return container;
        }
    
    }
    

    结果:

    class java.util.LinkedHashMap
    {messageDTOList=[{createdDate=1477396301061, message={"mac":-99,"device":444,"ifindex":55,"ip":668,"arpdevice":99,"aprifindex":"","vlanindex":111,"enddevice":133}, crudOperation=UPDATE}], senderServerId=5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b, messageId=5b36a7a1-c17d-471f-b16e-e8e0bf4afb5b20, rebasoftServerType=AC, taskType=MAC}
    

    有几点需要注意:由于消息在标头中没有类型信息(如果使用它,则会由出站转换器添加)消息被解码为简单的Map

    如果要解码成对象,要么需要headers中的类型信息,要么需要用类映射器配置转换器(如DefaultClassMapper

    其次,看起来“消息”元素是双重编码的 JSON - 它已经是 JSON 并且已经被重新编码)。

    【讨论】:

    • 感谢加里回来。我已将上面要求的信息添加到问题中。
    • 提供方的代码是用java编写的,我不介意将它作为一个简单的地图接收。我已尝试按照代码使用 DefaultClassMapper,但无济于事。我想知道非 Spring 生产者是否需要向 Spring 消费者提供标头信息?
    • 这是您的问题 jsonMessageConverter.setClassMapper(defaultClassMapper());defaultClassMapper.setDefaultType(byte.class); 您正试图将 JSON 解码为单个 byte。只需删除该类映射器,您就会得到一个 Map。
    • 我已经让它与仍在其中的代码一起工作,尽管它可能没有必要。如下所述,主要问题是我连接 MessageListenerAdapter 的方式。
    【解决方案2】:

    我发现了这个问题,我的配置类中的 MessageListenerAdapter 似乎不喜欢带有构造函数参数的 ClientHandler。一旦我删除了构造函数参数,它就可以正常工作了。

    【讨论】:

      【解决方案3】:

      您的有效负载/数据类中不需要参数构造函数。

      【讨论】:

        【解决方案4】:

        更新模型类以实现Serializable 接口为我解决了这个问题。

        【讨论】:

          猜你喜欢
          • 2018-11-24
          • 1970-01-01
          • 2020-10-22
          • 1970-01-01
          • 1970-01-01
          • 2015-03-03
          • 1970-01-01
          • 1970-01-01
          • 2011-02-27
          相关资源
          最近更新 更多