Spring Remoting mit AMQP

Spring Remoting mit AMQP

1. Überblick

Wir haben inprevious installments der Serie gesehen, wie wirSpring Remoting und die zugehörigen Technologien nutzen können, um synchroneRemote Procedure Calls über einem HTTP-Kanal zwischen einem Server und einem Client zu ermöglichen.

In diesem Artikel werden wirexplore Spring Remoting on top of AMQP, which makes it possible to execute synchronous RPC while leveraging a medium that is inherently asynchronous.

2. RabbitMQ installieren

Es gibt verschiedene Messagingsysteme, die mitAMQP kompatibel sind, die wir verwenden können, und wir wählenRabbitMQ, da es sich um eine bewährte Plattform handelt und inSpring – vollständig unterstützt wird. Beide Produkte werden von demselben Unternehmen verwaltet (Dreh- und Angelpunkt).

Wenn SieAMQP oderRabbitMQ nicht kennen, können Sie unserequick introduction lesen.

Der erste Schritt besteht also darin,RabbitMQ zu installieren und zu starten. Es gibt verschiedene Möglichkeiten, es zu installieren - wählen Sie einfach Ihre bevorzugte Methode gemäß den genannten Schrittenin the official guide.

3. Maven-Abhängigkeiten

Wir werden die Server- und Client-AnwendungenSpring Booteinrichten, um zu zeigen, wieAMQP Remotingfunktioniert. Wie so oft beiSpring Boot müssen wir nur die richtigen Starterabhängigkeiten auswählen und importieren,as explained here:


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

Wir haben diespring-boot-starter-tomcat explizit ausgeschlossen, da wir keinen eingebettetenHTTP-Server benötigen. Dies würde stattdessen automatisch gestartet, wennMaven alle transitiven Abhängigkeiten in den Klassenpfad importieren würde.

4. Serveranwendung

4.1. Stellen Sie den Service bereit

Wie wir in den vorherigen Artikeln gezeigt haben, werden wir einCabBookingService verfügbar machen, das einen wahrscheinlichen Remote-Dienst simuliert.

Let’s start by declaring a bean that implements the interface of the service we want to make remotely callable. Dies ist die Bean, die den Serviceaufruf auf der Serverseite tatsächlich ausführt:

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

Let’s then define the queue from which the server will retrieve invocations. In diesem Fall reicht es aus, einen Namen dafür anzugeben und ihn im Konstruktor anzugeben:

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

Wie wir bereits aus den vorherigen Artikeln wissen, istone of the main concepts of Spring Remoting is the Service Exporter die Komponente, die tatsächlichcollects the invocation requestsaus einer Quelle stammt - in diesem Fall eineRabbitMQ-Warteschlange ─and invokes the desired method on the service implementation.

In diesem Fall definieren wir einAmqpInvokerServiceExporter, das - wie Sie sehen können - einen Verweis auf einAmqpTemplate benötigt. DieAmqpTemplate-Klasse wird vonSpring Framework bereitgestellt und vereinfacht die Handhabung vonAMQP--kompatiblen Messagingsystemen auf die gleiche Weise, wieJdbcTemplate den Umgang mit Datenbanken erleichtert.

Wir werden eine solcheAmqpTemplate-Bohne nicht explizit definieren, da sie automatisch vom automatischen Konfigurationsmodul vonSpring Bootbereitgestellt wird:

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

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

Schließlich müssen wirdefine a container that has the responsibility to consume messages from the queue and forward them to some specified listener.

Wir werden dannconnect this container to the service exporter,, die wir im vorherigen Schritt erstellt haben,to allow it to receive the queued messages. Hier wirdConnectionFactory automatisch vonSpring Boot bereitgestellt, genauso wieAmqpTemplate:

@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. Aufbau

Denken Sie daran, die Dateiapplication.properties so einzurichten, dassSpring Boot die Basisobjekte konfigurieren kann. Offensichtlich hängen die Werte der Parameter auch davon ab, wieRabbitMQ installiert wurde.

Beispielsweise könnte die folgende Konfiguration sinnvoll sein, wennRabbitMQ auf demselben Computer ausgeführt wird, auf dem dieses Beispiel ausgeführt wird:

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

5. Client-Anwendung

5.1. Rufen Sie den Remote-Dienst auf

Lassen Sie uns jetzt den Kunden ansprechen. Wieder müssen wirdefine the queue where invocation messages will be written to. Wir müssen überprüfen, ob sowohl Client als auch Server denselben Namen verwenden.

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

Auf der Clientseite benötigen wir ein etwas komplexeres Setup als auf der Serverseite. Tatsächlich müssen wirdefine an Exchange mit den zugehörigenBinding verbinden:

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

Eine gute Einführung in die Hauptkonzepte vonRabbitMQ alsExchanges undBindings isthere verfügbar.

DaSpring BootAmqpTemplate nicht automatisch konfiguriert, müssen wir selbst eines einrichten und routing key angeben. Dabei müssen wir überprüfen, obrouting key undexchange mit dem übereinstimmen, mit demExchange im vorherigen Schritt definiert wurden:

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

Dann haben wir, wie bei anderen Implementierungen vonSpring Remoting,define a FactoryBean that will produce local proxies of the service that is remotely exposed. Nichts Besonderes, wir müssen nur die Schnittstelle des Remote-Service bereitstellen:

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

Wir können den Remote-Service jetzt so verwenden, als ob er als lokale Bean deklariert wäre:

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

5.2. Konfiguration

Auch für die Client-Anwendung müssen wir die Werte in derapplication.properties-Datei richtig auswählen. In einer allgemeinen Konfiguration stimmen diese genau mit denen überein, die auf der Serverseite verwendet werden.

5.3. Führen Sie das Beispiel aus

Dies sollte ausreichen, um den Remote-Aufruf durchRabbitMQ zu demonstrieren. Starten wir dann RabbitMQ, die Serveranwendung und die Clientanwendung, die den Remotedienst aufruft.

Was hinter den Kulissen passiert, ist, dass dieAmqpProxyFactoryBean will build a proxy that implements the CabBookingService.

Wenn eine Methode auf diesem Proxy aufgerufen wird, stellt sie eine Nachricht inRabbitMQ in die Warteschlange und gibt darin alle Parameter des Aufrufs und den Namen einer Warteschlange an, die zum Zurücksenden des Ergebnisses verwendet werden soll.

Die Nachricht wird ausAmqpInvokerServiceExporter verbraucht, die die eigentliche Implementierung aufrufen. Anschließend wird das Ergebnis in einer Nachricht gesammelt und in die Warteschlange gestellt, deren Name in der eingehenden Nachricht angegeben wurde.

DasAmqpProxyFactoryBean empfängt das Ergebnis zurück und gibt schließlich den Wert zurück, der ursprünglich auf der Serverseite erzeugt wurde.

6. Fazit

In diesem Artikel haben wir gesehen, wie wirSpring Remoting verwenden können, um RPC über einem Nachrichtensystem bereitzustellen.

Dies ist wahrscheinlich nicht der richtige Weg für die Hauptszenarien, in denen wir wahrscheinlich lieber die Asynchronität vonRabbitMQ nutzen möchten. In einigen ausgewählten und begrenzten Szenarien kann ein synchroner Aufruf jedoch leichter zu verstehen und schneller und einfacher zu entwickeln sein.

Wie üblich finden Sie die Quellen aufover on GitHub.