printemps de démarrage rabbitmq MappingJackson2MessageConverter objet personnalisé de conversion
Je suis en train de créer un simple ressort de démarrage d'application avec spring boot qui "produisent" des messages à un rabbitmq d'échange/de la file d'attente et un autre échantillon de printemps de démarrage de l'app que "consommer" ces messages.
J'ai donc deux applications (ou microservices si vous le souhaitez).
1) "producteur" microservice
2) "consommateur" microservice
Le "producteur" a 2 objets du domaine. Foo et Bar qui doivent être convertis en json et l'envoyer à rabbitmq.
Le "consommateur" doit recevoir et de les convertir au format json message dans un domaine Foo et Bar, respectivement.
Pour une raison que je ne peux pas faire cette simple tâche. Il n'y a pas beaucoup d'exemples à ce sujet.
Pour le message de convertisseur je veux utiliser org.springframework.de messagerie.convertisseur de.MappingJackson2MessageConverter
Voici ce que j'ai à ce jour:
PRODUCTEUR MICROSERVICE
package demo.producer;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class ProducerApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication.class, args);
}
@Bean
Queue queue() {
return new Queue("queue", false);
}
@Bean
TopicExchange exchange() {
return new TopicExchange("exchange");
}
@Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("queue");
}
@Bean
public MappingJackson2MessageConverter jackson2Converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
return converter;
}
@Autowired
private Sender sender;
@Override
public void run(String... args) throws Exception {
sender.sendToRabbitmq(new Foo(), new Bar());
}
}
@Service
class Sender {
@Autowired
private RabbitMessagingTemplate rabbitMessagingTemplate;
@Autowired
private MappingJackson2MessageConverter mappingJackson2MessageConverter;
public void sendToRabbitmq(final Foo foo, final Bar bar) {
this.rabbitMessagingTemplate.setMessageConverter(this.mappingJackson2MessageConverter);
this.rabbitMessagingTemplate.convertAndSend("exchange", "queue", foo);
this.rabbitMessagingTemplate.convertAndSend("exchange", "queue", bar);
}
}
class Bar {
public int age = 33;
}
class Foo {
public String name = "gustavo";
}
CONSOMMATEUR MICROSERVICE
package demo.consumer;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;
@SpringBootApplication
@EnableRabbit
public class ConsumerApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Autowired
private Receiver receiver;
@Override
public void run(String... args) throws Exception {
}
}
@Service
class Receiver {
@RabbitListener(queues = "queue")
public void receiveMessage(Foo foo) {
System.out.println("Received <" + foo.name + ">");
}
@RabbitListener(queues = "queue")
public void receiveMessage(Bar bar) {
System.out.println("Received <" + bar.age + ">");
}
}
class Foo {
public String name;
}
class Bar {
public int age;
}
Et ici est l'exception, j'obtiens:
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener method could not be invoked with the incoming message
Endpoint handler details:
Method [public void demo.consumer.Receiver.receiveMessage(demo.consumer.Bar)]
Bean [demo.consumer.Receiver@1672fe87]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:116)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:93)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:756)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:679)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:83)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:170)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1257)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1021)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1005)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:83)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1119)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.amqp.support.converter.MessageConversionException: Cannot handle message
... 13 common frames omitted
Caused by: org.springframework.messaging.converter.MessageConversionException: No converter found to convert to class demo.consumer.Bar, message=GenericMessage [payload=byte[10], headers={amqp_receivedRoutingKey=queue, amqp_receivedExchange=exchange, amqp_deliveryTag=1, amqp_deliveryMode=PERSISTENT, amqp_consumerQueue=queue, amqp_redelivered=false, id=87cf7e06-a78a-ddc1-71f5-c55066b46b11, amqp_consumerTag=amq.ctag-msWSwB4bYGWVO2diWSAHlw, contentType=application/json;charset=UTF-8, timestamp=1433989934574}]
at org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver.resolveArgument(PayloadArgumentResolver.java:115)
at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:127)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:100)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:113)
... 12 common frames omitted
L'exception affirme qu'il n'existe pas de convertisseur, et c'est vrai, mon problème est que je n'ai aucune idée de comment régler le MappingJackson2MessageConverter convertisseur dans le cas des consommateurs (veuillez noter que je veux utiliser org.springframework.de messagerie.convertisseur de.MappingJackson2MessageConverter et pas org.springframework.amqp.de soutien.convertisseur de.JsonMessageConverter)
Toutes les pensées ?
Juste au cas où, vous pouvez fourche cet exemple de projet:
https://github.com/gustavoorsi/rabbitmq-consumer-receiver
Dans cet exemple, il utilise org.springframework.amqp.de soutien.convertisseur de.Jackson2JsonMessageConverter (qui appartient à la dépendance printemps-amqp), dans mon cas, je veux utiliser org.springframework.de messagerie.convertisseur de.MappingJackson2MessageConverter (qui appartient au printemps de messagerie).
OriginalL'auteur Gustavo | 2015-06-11
Vous devez vous connecter pour publier un commentaire.
Ok, j'ai finalement obtenu ce travail.
Printemps utilise un PayloadArgumentResolver à extraire, convertir et définir le message converti au paramètre de méthode annotée avec @RabbitListener. En quelque sorte, nous devons définir la mappingJackson2MessageConverter dans cet objet.
Donc, le CONSOMMATEUR application, nous avons besoin de mettre en œuvre RabbitListenerConfigurer. En substituant configureRabbitListeners(RabbitListenerEndpointRegistrar greffier) nous pouvons personnaliser DefaultMessageHandlerMethodFactory, à cette usine nous avons mis le message converter, et l'usine va créer notre PayloadArgumentResolver avec la la bonne convertir.
Voici un extrait du code, j'ai aussi mis à jour le projet git.
ConsumerApplication.java
Donc, si vous exécutez le Producteur microservice il va ajouter 2 messages dans la file d'attente. Celui qui représentent un objet Foo et un autre, qui représentent une Barre d'objet.
En exécutant le consommateur microservice vous verrez que les deux sont consommés par la méthode de la Récepteur classe.
Mise à jour:
Il y a un problème conceptuel sur les files d'attente de mon côté, je pense. Ce que je voulais obtenu peut ne pas être possible en déclarant 2 méthodes annotées avec @RabbitListener qui pointe vers la même file d'attente. La solution ci-dessus ne fonctionnait pas correctement. Si vous envoyez à rabbitmq, disons, 6 Foo messages et 3 de la Barre des messages, elles ne seront pas reçu 6 fois par l'auditeur de Foo paramètre. Il semble que l'auditeur sont invoqués en parallèle, donc il n'y a aucun moyen de discrimination qui auditeur à invoquer basée sur la méthode de type d'argument.
Ma solution (et je ne suis pas sûr si c'est la meilleure façon, je suis ouvert à toute suggestion ici est de créer une file d'attente pour chaque entité.
Alors maintenant, j'ai file d'attente.bar et file d'attente.foo, et de mettre à jour @RabbitListener(files d'attente = "file d'attente.foo")
Encore une fois, j'ai mis à jour le code et vous pouvez le vérifier dans mon dépôt git.
@RabbitListener
avec@RabbitHandler
sur les différentes méthodes à l'appui de ce cas d'utilisation.c'est bon à savoir. merci !
OriginalL'auteur Gustavo
Ne l'ont pas fait moi-même, mais il semble que vous devez vous inscrire à la appropriée de conversions par la mise en place d'un RabbitTemplate. Jetez un oeil à la section 3.6.2 dans ce Printemps, la documentation. Je sais qu'il est configuré à l'aide de la AMQP les classes, mais si la messagerie de votre classe de mentionner est compatible il n'y a aucune raison que vous ne pouvez pas le remplacer. Ressemble cette référence explique comment vous pouvez le faire en utilisant Java configuration plutôt que XML. Je n'ai pas vraiment l'habitude de Lapin donc je n'ai pas d'expérience personnelle, mais j'aimerais entendre ce que vous trouver.
OriginalL'auteur Rob Baily