Spring Remoting с AMQP

Spring Remoting с AMQP

1. обзор

Мы видели вprevious installments серии, как мы можем использоватьSpring Remoting и связанные с ним технологии, чтобы включить синхронныйRemote Procedure Calls поверх HTTP-канала между сервером и клиентом.

В этой статье мы будемexplore Spring Remoting on top of AMQP, which makes it possible to execute synchronous RPC while leveraging a medium that is inherently asynchronous.

2. Установка RabbitMQ

Существуют различные системы обмена сообщениями, совместимые сAMQP, которые мы могли бы использовать, и мы выбралиRabbitMQ, потому что это проверенная платформа и полностью поддерживается вSpring –, оба продукта управляются одной и той же компанией. (Ключевой).

Если вы не знакомы сAMQP илиRabbitMQ, вы можете прочитать нашquick introduction.

Итак, первый шаг - установить и запуститьRabbitMQ. Есть несколько способов установить его - просто выберите предпочтительный метод, выполнив шаги, указанныеin the official guide.

3. Maven Зависимости

Мы собираемся настроить серверные и клиентские приложенияSpring Boot, чтобы показать, как работаетAMQP Remoting. Как это часто бывает сSpring Boot, нам просто нужно выбрать и импортировать правильные стартовые зависимости,as explained here:


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

Мы явно исключилиspring-boot-starter-tomcat, потому что нам не нужен какой-либо встроенный серверHTTP - он запускался бы автоматически, если бы мы разрешилиMaven импортировать все транзитивные зависимости в пути к классам.

4. Серверное приложение

4.1. Разоблачить сервис

Как мы показали в предыдущих статьях, мы представимCabBookingService, который имитирует вероятную удаленную службу.

Let’s start by declaring a bean that implements the interface of the service we want to make remotely callable. Это bean-компонент, который фактически будет выполнять вызов службы на стороне сервера:

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

Let’s then define the queue from which the server will retrieve invocations. В этом случае достаточно указать для него имя, указав его в конструкторе:

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

Как мы уже знаем из предыдущих статей,one of the main concepts of Spring Remoting is the Service Exporter, компонент, который на самом делеcollects the invocation requests из некоторого источника ─ в данном случае, очередьRabbitMQand invokes the desired method on the service implementation.

В этом случае мы определяемAmqpInvokerServiceExporter, который ─ как вы можете видеть ─ нуждается в ссылке наAmqpTemplate. КлассAmqpTemplate предоставляетсяSpring Framework и упрощает обработку совместимых сAMQP-систем обмена сообщениями так же, какJdbcTemplate упрощает работу с базами данных.

Мы не будем явно определять такой bean-компонентAmqpTemplate, потому что он будет автоматически предоставлен модулем автоконфигурацииSpring Boot:

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

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

Наконец, нам нужноdefine a container that has the responsibility to consume messages from the queue and forward them to some specified listener.

Затем мы создадимconnect this container to the service exporter, на предыдущем шаге,to allow it to receive the queued messages. ЗдесьConnectionFactory автоматически предоставляетсяSpring Boot так же, какAmqpTemplate:

@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. конфигурация

Не забудьте настроить файлapplication.properties, чтобы позволитьSpring Boot настраивать основные объекты. Очевидно, что значения параметров также будут зависеть от способа установкиRabbitMQ.

Например, следующая конфигурация может быть разумной, когдаRabbitMQ запускает тот же компьютер, на котором выполняется этот пример:

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

5. Клиентское приложение

5.1. Вызов удаленной службы

Теперь займемся клиентом. Опять же, нам нужноdefine the queue where invocation messages will be written to. Нам нужно еще раз проверить, что и клиент, и сервер используют одно и то же имя.

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

На стороне клиента нам нужна чуть более сложная настройка, чем на стороне сервера. Фактически, нам нужноdefine an Exchange с соответствующимBinding:

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

Доступно хорошее введение в основные концепцииRabbitMQ какExchanges иBindingshere.

ПосколькуSpring Boot не настраивает автоматическиAmqpTemplate, мы должны настроить его сами, указав routing key. При этом нам нужно дважды проверить, чтоrouting key иexchange совпадают с тем, который использовался для определенияExchange на предыдущем шаге:

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

Затем, как и с другими реализациямиSpring Remoting, мыdefine a FactoryBean that will produce local proxies of the service that is remotely exposed. Ничего особенного, нам просто нужно предоставить интерфейс удаленного сервиса:

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

Теперь мы можем использовать удаленный сервис, как если бы он был объявлен как локальный компонент:

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

5.2. Настроить

Также для клиентского приложения мы должны правильно выбрать значения в файлеapplication.properties. В обычной настройке они будут точно соответствовать тем, которые используются на стороне сервера.

5.3. Запустите пример

Этого должно быть достаточно, чтобы продемонстрировать удаленный вызов черезRabbitMQ. Затем запустим RabbitMQ, серверное приложение и клиентское приложение, которое вызывает удаленную службу.

За кадром происходит то, чтоAmqpProxyFactoryBean will build a proxy that implements the CabBookingService.

Когда на этом прокси-сервере вызывается метод, он ставит в очередь сообщение наRabbitMQ, указывая в нем все параметры вызова и имя очереди, которая будет использоваться для обратной отправки результата.

Сообщение получено отAmqpInvokerServiceExporter, который вызывает фактическую реализацию. Затем он собирает результат в сообщении и помещает его в очередь, имя которой было указано во входящем сообщении.

AmqpProxyFactoryBean получает обратно результат и, наконец, возвращает значение, которое было изначально создано на стороне сервера.

6. Заключение

В этой статье мы увидели, как мы можем использоватьSpring Remoting для предоставления RPC поверх системы обмена сообщениями.

Вероятно, это не путь для основных сценариев, в которых мы, вероятно, предпочитаем использовать асинхронностьRabbitMQ, но в некоторых избранных и ограниченных сценариях синхронный вызов может быть проще для понимания, а также быстрее и проще разработать.

Как обычно, вы найдете источники наover on GitHub.

Related