1. Обзор
Мы видели в предыдущих частях серии, как мы можем использовать Spring Remoting и связанные с этим технологии для включения синхронных Удаленных вызовов процедур поверх HTTP-канала между сервером и клиентом.
В этой статье мы исследуем Spring Remoting поверх AMQP , что позволяет выполнять синхронный RPC при использовании среды , которая по своей сути асинхронна .
2. Установка RabbitMQ
Существуют различные системы обмена сообщениями, совместимые с AMQP , которые мы могли бы использовать, и мы выбираем RabbitMQ , потому что это проверенная платформа и она полностью поддерживается в Spring – оба продукта управляются одной и той же компанией (Pivotal).
Если вы не знакомы с AMQP или RabbitMQ , вы можете прочитать наше краткое введение .
Итак, первый шаг-установить и запустить RabbitMQ . Существуют различные способы его установки – просто выберите предпочтительный метод, следуя инструкциям, указанным в официальном руководстве .
3. Зависимости Maven
Мы собираемся настроить сервер и клиент Spring Boot приложения, чтобы показать, как работает AMQP Remoting . Как это часто бывает с Spring Boot , нам просто нужно выбрать и импортировать правильные стартовые зависимости, как описано здесь :
org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-tomcat
Мы явно исключили spring-boot-starter-tomcat , потому что нам не нужен встроенный HTTP сервер – он будет автоматически запущен, если мы разрешим Maven импортировать все транзитивные зависимости в пути к классу.
4. Серверное приложение
4.1. Предоставление Услуг
Как мы показали в предыдущих статьях, будет представлена Служба бронирования такси , которая имитирует вероятную удаленную службу.
Давайте начнем с объявления компонента, который реализует интерфейс службы, которую мы хотим сделать удаленно вызываемой. Это компонент, который фактически выполнит вызов службы на стороне сервера:
@Bean CabBookingService bookingService() { return new CabBookingServiceImpl(); }
Затем давайте определим очередь, из которой сервер будет извлекать вызовы. В этом случае достаточно указать для него имя, предоставив его в конструкторе:
@Bean Queue queue() { return new Queue("remotingQueue"); }
Как мы уже знаем из предыдущих статей, одной из основных концепций Spring Remoting является Экспортер услуг , компонент, который на самом деле собирает запросы на вызов из некоторого источника ─ в данном случае RabbitMQ очередь ─ и вызывает нужный метод реализации службы .
В этом случае мы определяем AmqpInvokerServiceExporter , который, как вы можете видеть, нуждается в ссылке на AmqpTemplate . Класс AmqpTemplate предоставляется Spring Framework и облегчает обработку AMQP- совместимых систем обмена сообщениями так же, как JdbcTemplate облегчает работу с базами данных.
Мы не будем явно определять такой AmqpTemplate bean, потому что он будет автоматически предоставлен модулем автоматической настройки 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; }
Наконец, нам нужно определить контейнер , который несет ответственность за получение сообщений из очереди и пересылку их какому-либо указанному слушателю .
Затем мы подключим этот контейнер к экспортеру услуг, который мы создали на предыдущем шаге, чтобы позволить ему получать сообщения в очереди . Здесь 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. Вызовите Удаленную службу
Давайте займемся клиентом прямо сейчас. Опять же, нам нужно определить очередь, в которую будут записываться сообщения вызова|/. Нам нужно дважды проверить, что и клиент, и сервер используют одно и то же имя.
@Bean Queue queue() { return new Queue("remotingQueue"); }
На стороне клиента нам нужна немного более сложная настройка, чем на стороне сервера. На самом деле нам нужно определить Обмен с соответствующей привязкой :
@Bean Exchange directExchange(Queue someQueue) { DirectExchange exchange = new DirectExchange("remoting.exchange"); BindingBuilder .bind(someQueue) .to(exchange) .with("remoting.binding"); return exchange; }
Хорошее введение в основные понятия RabbitMQ как Обмены и Привязки доступно здесь .
Поскольку Spring Boot не выполняет автоматическую настройку AmqpTemplate , мы должны настроить его сами, указав ключ маршрутизации r /. При этом нам нужно дважды проверить, что ключ маршрутизации и exchange совпадают с тем, который использовался для определения Exchange на предыдущем шаге:
@Bean RabbitTemplate amqpTemplate(ConnectionFactory factory) { RabbitTemplate template = new RabbitTemplate(factory); template.setRoutingKey("remoting.binding"); template.setExchange("remoting.exchange"); return template; }
Затем, как и в случае с другими реализациями Spring Remoting , мы определяем FactoryBean , который будет создавать локальные прокси-серверы удаленно предоставляемой службы . Здесь нет ничего особенного, нам просто нужно предоставить интерфейс удаленного сервиса:
@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, серверное приложение и клиентское приложение, которое вызывает удаленную службу.
Что происходит за кулисами, так это то, что Amqp ProxyFactoryBean создаст прокси-сервер, реализующий CabBookingService .
Когда метод вызывается на этом прокси-сервере, он помещает сообщение в очередь на RabbitMQ , указывая в нем все параметры вызова и имя очереди, которая будет использоваться для отправки результата.
Сообщение потребляется от экспортера службы вызова Amqp , который вызывает фактическую реализацию. Затем он собирает результат в сообщение и помещает его в очередь, имя которой было указано во входящем сообщении.
Amqp ProxyFactoryBean получает обратно результат и, наконец, возвращает значение, первоначально созданное на стороне сервера.
6. Заключение
В этой статье мы рассмотрели, как мы можем использовать Spring Remoting для предоставления RPC поверх системы обмена сообщениями.
Вероятно , это не тот путь для основных сценариев, в которых мы, вероятно, предпочитаем использовать асинхронность RabbitMQ , но в некоторых выбранных и ограниченных сценариях синхронный вызов может быть проще для понимания, быстрее и проще в разработке.
Как обычно, вы найдете источники на на GitHub .