Comunicação remota com AMQP

Comunicação remota com AMQP

1. Visão geral

Vimos emprevious installments da série como podemos aproveitarSpring Remoting e as tecnologias relacionadas para habilitarRemote Procedure Calls síncrono em cima de um canal HTTP entre um servidor e um cliente.

Neste artigo, vamosexplore Spring Remoting on top of AMQP, which makes it possible to execute synchronous RPC while leveraging a medium that is inherently asynchronous.

2. Instalando RabbitMQ

Existem vários sistemas de mensagens que são compatíveis comAMQP que poderíamos usar, e escolhemosRabbitMQ porque é uma plataforma comprovada e é totalmente compatível comSpring – ambos os produtos são gerenciados pela mesma empresa (Pivotal).

Se você não está familiarizado comAMQP ouRabbitMQ, pode ler nossoquick introduction.

Portanto, a primeira etapa é instalar e iniciarRabbitMQ. Existem várias maneiras de instalá-lo - basta escolher seu método preferido seguindo as etapas mencionadasin the official guide.

3. Dependências do Maven

Vamos configurar os aplicativos de servidor e clienteSpring Boot para mostrar comoAMQP Remoting funciona. Como costuma ser o caso comSpring Boot, só precisamos escolher e importar as dependências iniciais corretas,as explained here:


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

Excluímos explicitamente ospring-boot-starter-tomcat porque não precisamos de nenhum servidorHTTP incorporado - que seria iniciado automaticamente se permitíssemosMaven importar todas as dependências transitivas no caminho de classe.

4. Aplicativo de servidor

4.1. Exponha o serviço

Como mostramos nos artigos anteriores, exporemos umCabBookingService que simula um provável serviço remoto.

Let’s start by declaring a bean that implements the interface of the service we want to make remotely callable. Este é o bean que realmente executará a chamada de serviço no lado do servidor:

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

Let’s then define the queue from which the server will retrieve invocations. Basta, neste caso, especificar um nome para ele, fornecendo-o no construtor:

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

Como já sabemos dos artigos anteriores,one of the main concepts of Spring Remoting is the Service Exporter, o componente que realmentecollects the invocation requests de alguma fonte ─ neste caso, uma filaRabbitMQand invokes the desired method on the service implementation.

Nesse caso, definimos umAmqpInvokerServiceExporter que ─ como você pode ver ─ precisa de uma referência a umAmqpTemplate. A classeAmqpTemplate é fornecida peloSpring Frameworke facilita o manuseio de sistemas de mensagensAMQP-não compatíveis da mesma forma queJdbcTemplate facilita o tratamento de bancos de dados.

Não definiremos explicitamente esse beanAmqpTemplate porque ele será fornecido automaticamente pelo módulo de configuração automática 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;
}

Finalmente, precisamosdefine a container that has the responsibility to consume messages from the queue and forward them to some specified listener.

Em seguida, iremosconnect this container to the service exporter, que criamos na etapa anterior,to allow it to receive the queued messages. Aqui,ConnectionFactory é fornecido automaticamente porSpring Boot da mesma forma queAmqpTemplate é:

@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. Configuração

Vamos lembrar de configurar o arquivoapplication.properties para permitir queSpring Boot configure os objetos básicos. Obviamente, os valores dos parâmetros também dependerão da maneira comoRabbitMQ foi instalado.

Por exemplo, o seguinte pode ser uma configuração razoável quandoRabbitMQ executa na mesma máquina em que este exemplo é executado:

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

5. Aplicativo Cliente

5.1. Chame o serviço remoto

Vamos abordar o cliente agora. Novamente, precisamosdefine the queue where invocation messages will be written to. Precisamos verificar novamente se o cliente e o servidor usam o mesmo nome.

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

No lado do cliente, precisamos de uma configuração um pouco mais complexa do que no lado do servidor. Na verdade, precisamosdefine an Exchange com osBinding relacionados:

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

Uma boa introdução sobre os conceitos principais deRabbitMQ comoExchangeseBindings está disponívelhere.

ComoSpring Boot não configura automaticamenteAmqpTemplate, devemos configurar um nós mesmos, especificando um routing key. Ao fazer isso, precisamos verificar novamente serouting keyeexchange correspondem ao usado para definirExchange na etapa anterior:

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

Então, como fizemos com outras implementações deSpring Remoting, nósdefine a FactoryBean that will produce local proxies of the service that is remotely exposed. Nada muito chique aqui, só precisamos fornecer a interface do serviço remoto:

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

Agora podemos usar o serviço remoto como se fosse declarado como um bean local:

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

5.2. Configuração

Também para a aplicação cliente, temos que escolher corretamente os valores no arquivoapplication.properties. Em uma configuração comum, elas corresponderiam exatamente às usadas no lado do servidor.

5.3. Execute o exemplo

Isso deve ser suficiente para demonstrar a chamada remota por meio deRabbitMQ. Vamos então iniciar o RabbitMQ, o aplicativo de servidor e o aplicativo cliente que invoca o serviço remoto.

O que acontece nos bastidores é queAmqpProxyFactoryBean will build a proxy that implements the CabBookingService.

Quando um método é invocado nesse proxy, ele enfileira uma mensagem emRabbitMQ, especificando nele todos os parâmetros da invocação e um nome de uma fila a ser usada para devolver o resultado.

A mensagem é consumida deAmqpInvokerServiceExporter que invoca a implementação real. Em seguida, ele coleta o resultado em uma mensagem e o coloca na fila, cujo nome foi especificado na mensagem recebida.

OAmqpProxyFactoryBean recebe de volta o resultado e, finalmente, retorna o valor que foi originalmente produzido no lado do servidor.

6. Conclusão

Neste artigo, vimos como podemos usarSpring Remoting para fornecer RPC no topo de um sistema de mensagens.

Provavelmente não é o caminho a percorrer para os cenários principais em que provavelmente preferimos aproveitar a assincronicidade deRabbitMQ, mas em alguns cenários selecionados e limitados, uma chamada síncrona pode ser mais fácil de entender e mais rápida e simples de desenvolver.

Como de costume, você encontrará as fontes emover on GitHub.