Spring Remoting avec AMQP

Spring Remoting avec AMQP

1. Vue d'ensemble

Nous avons vu dans lesprevious installments de la série comment nous pouvons tirer parti deSpring Remoting et des technologies associées pour activer desRemote Procedure Calls synchrones sur un canal HTTP entre un serveur et un client.

Dans cet article, nous allonsexplore Spring Remoting on top of AMQP, which makes it possible to execute synchronous RPC while leveraging a medium that is inherently asynchronous.

2. Installation de RabbitMQ

Il existe différents systèmes de messagerie compatibles avecAMQP que nous pourrions utiliser, et nous choisissonsRabbitMQ car c'est une plate-forme éprouvée et entièrement prise en charge dansSpring –, les deux produits sont gérés par la même société (Pivot).

Si vous ne connaissez pasAMQP ouRabbitMQ, vous pouvez lire nosquick introduction.

La première étape consiste donc à installer et démarrerRabbitMQ. Il existe différentes façons de l'installer - choisissez simplement votre méthode préférée en suivant les étapes mentionnéesin the official guide.

3. Dépendances Maven

Nous allons configurer les applications serveur et clientSpring Boot pour montrer comment fonctionneAMQP Remoting. Comme c'est souvent le cas avecSpring Boot, il suffit de choisir et d'importer les bonnes dépendances de démarrage,as explained here:


    org.springframework.boot
    spring-boot-starter-amqp
    
        
            org.springframework.boot
            spring-boot-starter-tomcat
        
    

Nous avons explicitement exclu lesspring-boot-starter-tomcat car nous n'avons besoin d'aucun serveur intégréHTTP - qui serait automatiquement démarré à la place si nous permettions àMaven d'importer toutes les dépendances transitives dans le chemin de classe.

4. Application serveur

4.1. Exposez le service

Comme nous l'avons montré dans les articles précédents, nous allons exposer unCabBookingService qui simule un service distant probable.

Let’s start by declaring a bean that implements the interface of the service we want to make remotely callable. C'est le bean qui exécutera réellement l'appel de service côté serveur:

@Bean
CabBookingService bookingService() {
    return new CabBookingServiceImpl();
}

Let’s then define the queue from which the server will retrieve invocations. Il suffit, dans ce cas, de lui spécifier un nom, en le fournissant dans le constructeur:

@Bean
Queue queue() {
    return new Queue("remotingQueue");
}

Comme nous le savons déjà dans les articles précédents,one of the main concepts of Spring Remoting is the Service Exporter, le composant qui en faitcollects the invocation requests à partir d'une source - dans ce cas, une file d'attenteRabbitMQand invokes the desired method on the service implementation.

Dans ce cas, nous définissons unAmqpInvokerServiceExporter qui ─ comme vous pouvez le voir ─ a besoin d'une référence à unAmqpTemplate. La classeAmqpTemplate est fournie par lesSpring Framework et facilite la gestion des systèmes de messagerie compatiblesAMQP-de la même manière que lesJdbcTemplate facilitent le traitement des bases de données.

Nous ne définirons pas explicitement ce beanAmqpTemplate car il sera automatiquement fourni par le module de configuration automatique deSpring Boot:

@Bean AmqpInvokerServiceExporter exporter(
  CabBookingService implementation, AmqpTemplate template) {

    AmqpInvokerServiceExporter exporter = new AmqpInvokerServiceExporter();
    exporter.setServiceInterface(CabBookingService.class);
    exporter.setService(implementation);
    exporter.setAmqpTemplate(template);
    return exporter;
}

Enfin, nous avons besoin dedefine a container that has the responsibility to consume messages from the queue and forward them to some specified listener.

Nous allons ensuiteconnect this container to the service exporter, que nous avons créés à l'étape précédente,to allow it to receive the queued messages. Ici, leConnectionFactory est automatiquement fourni parSpring Boot de la même manière que leAmqpTemplate est:

@Bean
SimpleMessageListenerContainer listener(
  ConnectionFactory facotry,
  AmqpInvokerServiceExporter exporter,
  Queue queue) {

    SimpleMessageListenerContainer container
     = new SimpleMessageListenerContainer(facotry);
    container.setMessageListener(exporter);
    container.setQueueNames(queue.getName());
    return container;
}

4.2. Configuration

N'oublions pas de configurer le fichierapplication.properties pour permettre àSpring Boot de configurer les objets de base. Évidemment, les valeurs des paramètres dépendront également de la façon dontRabbitMQ a été installé.

Par exemple, la configuration suivante pourrait être une configuration raisonnable lorsqueRabbitMQ l'exécute sur la même machine que celle où cet exemple s'exécute:

spring.rabbitmq.dynamic=true
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.host=localhost

5. Application client

5.1. Appeler le service distant

Attaquons-nous maintenant au client. Encore une fois, nous avons besoin dedefine the queue where invocation messages will be written to. Nous devons vérifier que le client et le serveur utilisent le même nom.

@Bean
Queue queue() {
    return new Queue("remotingQueue");
}

Côté client, nous avons besoin d’une configuration légèrement plus complexe que celle du serveur. En fait, nous devonsdefine an Exchange avec lesBinding associés:

@Bean
Exchange directExchange(Queue someQueue) {
    DirectExchange exchange = new DirectExchange("remoting.exchange");
    BindingBuilder
      .bind(someQueue)
      .to(exchange)
      .with("remoting.binding");
    return exchange;
}

Une bonne introduction sur les principaux concepts deRabbitMQ commeExchanges etBindings est disponiblehere.

PuisqueSpring Boot ne configure pas automatiquement lesAmqpTemplate, nous devons en créer un nous-mêmes, en spécifiant un routing key. Ce faisant, nous devons revérifier que lesrouting key et lesexchange correspondent à celui utilisé pour définir lesExchange à l'étape précédente:

@Bean RabbitTemplate amqpTemplate(ConnectionFactory factory) {
    RabbitTemplate template = new RabbitTemplate(factory);
    template.setRoutingKey("remoting.binding");
    template.setExchange("remoting.exchange");
    return template;
}

Ensuite, comme nous l'avons fait avec les autres implémentations deSpring Remoting, nousdefine a FactoryBean that will produce local proxies of the service that is remotely exposed. Rien d'extraordinaire ici, il suffit de fournir l'interface du service distant:

@Bean AmqpProxyFactoryBean amqpFactoryBean(AmqpTemplate amqpTemplate) {
    AmqpProxyFactoryBean factoryBean = new AmqpProxyFactoryBean();
    factoryBean.setServiceInterface(CabBookingService.class);
    factoryBean.setAmqpTemplate(amqpTemplate);
    return factoryBean;
}

Nous pouvons maintenant utiliser le service distant comme s'il était déclaré comme un bean local:

CabBookingService service = context.getBean(CabBookingService.class);
out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));

5.2. Installer

Aussi pour l'application cliente, nous devons choisir correctement les valeurs dans le fichierapplication.properties. Dans une configuration commune, ceux-ci correspondraient exactement à ceux utilisés côté serveur.

5.3. Exécutez l'exemple

Cela devrait être suffisant pour démontrer l'invocation à distance viaRabbitMQ. Lançons ensuite RabbitMQ, l’application serveur et l’application cliente qui appelle le service distant.

Ce qui se passe dans les coulisses, c'est que lesAmqpProxyFactoryBean will build a proxy that implements the CabBookingService.

Lorsqu'une méthode est appelée sur ce proxy, elle met en file d'attente un message sur lesRabbitMQ, en y spécifiant tous les paramètres de l'invocation et un nom de file d'attente à utiliser pour renvoyer le résultat.

Le message est consommé à partir desAmqpInvokerServiceExporter qui invoquent l'implémentation réelle. Il collecte ensuite le résultat dans un message et le place dans la file d'attente dont le nom a été spécifié dans le message entrant.

LeAmqpProxyFactoryBean reçoit le résultat et, enfin, retourne la valeur qui a été initialement produite côté serveur.

6. Conclusion

Dans cet article, nous avons vu comment nous pouvons utiliserSpring Remoting pour fournir RPC au-dessus d'un système de messagerie.

Ce n’est probablement pas la voie à suivre pour les principaux scénarios où nous préférons probablement tirer parti de l’asynchronisme deRabbitMQ, mais dans certains scénarios sélectionnés et limités, un appel synchrone peut être plus facile à comprendre et plus rapide et plus simple à développer.

Comme d'habitude, vous trouverez les sources surover on GitHub.