简体   繁体   English

如何在 Spring Boot 应用程序中使用 spring-rabbit 处理 JSON 消息?

[英]How to deal with JSON message with spring-rabbit in spring boot application?

Here are my code snippets.这是我的代码片段。

  • MQConfiguration class for configuration用于配置的MQConfiguration

    @Configuration public class MQConfiguration { @Bean public Receiver receiver() { return new Receiver(); } }
  • Receiver class for dealing with receiving messages Receiver类,用于处理接收消息

    @RabbitListener(queues = "testMQ") public class Receiver { @RabbitHandler public void receive(Message msg){ System.out.println(msg.toString()); } }
  • And here is the JSON message I sent to the RabbitMQ这是我发送给RabbitMQ的 JSON 消息

    { "id": 1, "name": "My Name", "description": "This is description about me" }

However I got following error message when I ran my application.但是,当我运行我的应用程序时,我收到了以下错误消息。

2017-02-28 17:16:35.931  WARN 11828 --- [cTaskExecutor-1] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.

org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:872) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:782) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:702) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:95) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:186) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1227) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:683) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1181) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1165) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1500(SimpleMessageListenerContainer.java:95) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1367) [spring-rabbit-1.7.0.RELEASE.jar:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_60]
Caused by: org.springframework.amqp.AmqpException: No method found for class [B
    at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.getHandlerForPayload(DelegatingInvocableHandler.java:127) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.getMethodNameFor(DelegatingInvocableHandler.java:224) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.getMethodAsString(HandlerAdapter.java:61) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:140) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:106) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:779) ~[spring-rabbit-1.7.0.RELEASE.jar:na]
    ... 10 common frames omitted

So what should I do if all I want is to print the JSON message in receive() method?那么如果我只想在receive()方法中打印 JSON 消息,我该怎么办? I'd really appreciate that anyone can shed a light on this.我真的很感激任何人都可以对此有所了解。 :) :)

If you use Spring Boot, you just need to configure:如果使用Spring Boot,只需要配置:

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

Otherwise you have to configure:否则你必须配置:

@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
...
    factory.setMessageConverter(new Jackson2JsonMessageConverter());
...
    return factory;
}

http://docs.spring.io/spring-amqp/docs/1.7.0.RELEASE/reference/html/_reference.html#async-annotation-driven http://docs.spring.io/spring-amqp/docs/1.7.0.RELEASE/reference/html/_reference.html#async-annotation-driven

In order to send JSON to RabbitMQ and consume it by Spring Boot we need to set content_type .为了将 JSON 发送到 RabbitMQ 并由 Spring Boot 使用它,我们需要设置content_type

Let me describe with an example where I had a Python Producer and Java consumer (I was sending the JSON from Python to RabbitMQ and Spring Boot Java was supposed to receive the JSON task).让我用一个例子来描述我有一个 Python 生产者和 Java 消费者(我将 JSON 从 Python 发送到 RabbitMQ 并且 Spring Boot Java 应该接收 JSON 任务)。

There are two solutions:有两种解决方案:

Solution 1: Sending as JSON string and convert it manually using Jakson or GSON解决方案 1:作为 JSON 字符串发送并使用 Jakson 或 GS​​ON 手动转换

You need to set the content_type="text/plain" and convert the JSON to a string.Then in the Spring side, use a fuction with a string as the input as the listener and manually convert the object.需要设置 content_type="text/plain" 并将 JSON 转换为字符串。然后在 Spring 端,使用一个以字符串作为输入的函数作为侦听器并手动转换对象。

RabbitHandler: RabbitHandler:

@RabbitHandler
public void receive(String inputString) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    SimStatusReport theResult = objectMapper.readValue(inputString, SimStatusReport.class);

    System.out.println("String instance "  + theResult.toString() +
            " [x] Received");
}

SimStatusReport Object: SimStatusReport 对象:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SimStatusReport {
    private String id;
    private int t;
}

Here is my Python Code:这是我的 Python 代码:

import pika
import json
import uuid


connectionResult = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channelResult = connectionResult.channel()
routing_key_result = 'sim_results'
channelResult.queue_declare(queue=routing_key_result, durable=True)

def publish_result(sim_status):
    message =json.dumps(sim_status)
    channelResult.basic_publish(exchange='',
                                routing_key=routing_key_result,
                                body=message,
                                properties=pika.BasicProperties(
                                    content_type="text/plain",
                                    content_encoding= 'UTF-8',
                                    delivery_mode=2,  # make message persistent
                          ))
    print("Sent ", message)


newsim_status = {'id': str(uuid.uuid4()), 't': 0}
publish_result(newsim_status)

Solution 2: Sending JSON string and let the Jackson2JsonMessageConverter do the conversion for you automatically.解决方案 2:发送 JSON 字符串并让 Jackson2JsonMessageConverter 自动为您进行转换。

You need to set the content_type="application/json".您需要设置 content_type="application/json"。 Then you need to add the appropriate header to __TypeId__ in the header of the RabbitMQ request.然后需要在RabbitMQ请求的header中的__TypeId__中添加相应的header。 You need to include the exact name space of the object so the Jackson undestand the the conversion.您需要包含对象的确切名称空间,以便 Jackson 理解转换。

Here is my example using Python (just the publish_result fuction):这是我使用 Python 的示例(只是 publish_result 函数):

def publish_result(sim_status):
    message =json.dumps(sim_status)
    channelResult.basic_publish(exchange='',
                                routing_key=routing_key_result,
                                body=message,
                                properties=pika.BasicProperties(
                                    content_type="application/json"
                                    headers={'__TypeId__': 'com.zarinbal.simtest.run.model.SimStatusReport'},
                                    content_encoding= 'UTF-8',
                                    delivery_mode=2,  # make message persistent
                          ))
    print("Sent ", message)

Then you need to configure the Java to use Jackson2JsonMessageConverter:然后你需要配置Java来使用Jackson2JsonMessageConverter:

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

Here would be your listener:这是你的听众:

@RabbitListener(queues = "sim_results")
public class TaskReceiver {
    @RabbitHandler
    public void receive(SimStatusReport in) {
        System.out.println("Object instance "  + in +
                " [x] Received");
    }
}

Note: Make sure all you objects has setter and getters for all properties and all argument constructor.注意:确保所有对象都具有所有属性和所有参数构造函数的 setter 和 getter。 I use @Data, @NoArgsConstructor and @AllArgsConstructor from lombok to automatically generate it我使用 lombok 的 @Data、@NoArgsConstructor 和 @AllArgsConstructor 来自动生成它

I know its been awhile since the last response, but I'd want to add my answer.我知道自上次回复以来已经有一段时间了,但我想添加我的答案。 Just in case someone else is having the same issues.以防万一其他人遇到同样的问题。

When I set up a receiver like yours, I experienced the similar problem where I couldn't convert JSON in the body of the RabbitMQ Message to my object.当我设置像你这样的接收器时,我遇到了类似的问题,我无法将 RabbitMQ 消息正文中的 JSON 转换为我的对象。 It simply returns an error message like this.它只是返回这样的错误消息。

org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Failed to convert message
        at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:146) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1654) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1573) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1561) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1552) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1496) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:968) [spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:914) [spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83) [spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1289) [spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1195) [spring-rabbit-2.3.9.jar:2.3.9]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_271]
Caused by: org.springframework.amqp.AmqpException: No method found for class java.util.LinkedHashMap
        at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.getHandlerForPayload(DelegatingInvocableHandler.java:185) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.getMethodFor(DelegatingInvocableHandler.java:317) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.getMethodFor(HandlerAdapter.java:110) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandlerAndProcessResult(MessagingMessageListenerAdapter.java:192) ~[spring-rabbit-2.3.9.jar:2.3.9]
        at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:137) ~[spring-rabbit-2.3.9.jar:2.3.9]
        ... 11 common frames omitted

After hours of searching through different websites without any good answer, I finally found out that @RabbitListener won't work at the class level.经过数小时在不同网站上搜索但没有任何好的答案,我终于发现@RabbitListener 在课堂级别不起作用。 When you move it to the method level, though, it works fine.但是,当您将其移动到方法级别时,它可以正常工作。

So, I remove @RabbitHandler and move @RabbitListener to the receive method.所以,我删除了@RabbitHandler 并将@RabbitListener 移到了接收方法中。

Your receiver code should look like this after editing.编辑后,您的接收器代码应如下所示。

public class Receiver {

    @RabbitListener(queues = "testMQ")
    public void receive(Message msg){
        System.out.println(msg.toString());
    }
}

I still don't fully understand why it is not working at the class level.我仍然不完全理解为什么它在课堂上不起作用。 If anyone has a good explanation, please share it in the comments section.如果有人有好的解释,请在评论部分分享。 Thank you very much.非常感谢。

Source: https://titanwolf.org/Network/Articles/Article?AID=0f214d79-10f0-4b4c-9478-607428770256#gsc.tab=0来源: https : //titanwolf.org/Network/Articles/Article?AID=0f214d79-10f0-4b4c-9478-607428770256#gsc.tab=0

in Simple way you can use this function new String(Messages)以简单的方式,您可以使用此函数new String(Messages)

@RabbitListener(queues = "testMQ")
public class Receiver {

    @RabbitHandler
    public void receive(Message msg){
String MQMessage = new String(msg.getBody());
        System.out.println(MQMessage);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM