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
, нам просто нужно выбрать и импортировать правильные начальные зависимости, как описано здесь :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
Мы явно исключили spring-boot-starter-tomcat
, потому что нам не нужен какой-либо встроенный HTTP
- сервер — вместо этого он будет запускаться автоматически, если мы позволим Maven
импортировать все транзитивные зависимости в пути к классам.
4. Серверное приложение
4.1. Разоблачить сервис
Как мы показали в предыдущих статьях, мы предоставим CabBookingService
, который имитирует вероятный удаленный сервис.
Начнем с объявления bean-компонента, реализующего интерфейс службы, которую мы хотим сделать доступной для удаленного вызова. Это bean-компонент, который фактически выполнит вызов службы на стороне сервера:
@Bean
CabBookingService bookingService() {
return new CabBookingServiceImpl();
}
Давайте затем определим очередь, из которой сервер будет извлекать вызовы. В этом случае достаточно указать для него имя, предоставив его в конструкторе:
@Bean
Queue queue() {
return new Queue("remotingQueue");
}
Как мы уже знаем из предыдущих статей, одна из основных концепций Spring Remoting
— это Service Exporter
, компонент, который фактически собирает запросы на вызов из какого-то источника — в данном случае из очереди RabbitMQ —
и вызывает нужный метод в сервисе. реализация .
В этом случае мы определяем 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;
}
Наконец, нам нужно определить контейнер
, который отвечает за получение сообщений из очереди и пересылку их определенному слушателю .
Затем мы подключим этот контейнер
к экспортеру служб,
созданному на предыдущем шаге, чтобы он мог получать сообщения из очереди . Здесь 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");
}
На стороне клиента нам нужна немного более сложная настройка, чем на стороне сервера. На самом деле нам нужно определить 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
и Bindings
доступно здесь .
Поскольку Spring Boot
не настраивает AmqpTemplate автоматически
, мы должны настроить его самостоятельно, указав ключ ar outing
. При этом нам нужно дважды проверить, совпадают ли ключ маршрутизации
и биржа
с той, которая использовалась для определения биржи
на предыдущем шаге:
@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, серверное приложение и клиентское приложение, которое вызывает удаленную службу.
Что происходит за кулисами, так это то, что AmqpProxyFactoryBean
создаст прокси, который реализует CabBookingService
.
Когда метод вызывается на этом прокси, он ставит сообщение в очередь на RabbitMQ
, указывая в нем все параметры вызова и имя очереди, которая будет использоваться для отправки обратно результата.
Сообщение потребляется из AmqpInvokerServiceExporter
, который вызывает реальную реализацию. Затем он собирает результат в сообщении и помещает его в очередь, имя которой было указано во входящем сообщении.
AmqpProxyFactoryBean получает
результат и, наконец, возвращает значение, которое было первоначально создано на стороне сервера.
6. Заключение
В этой статье мы увидели, как мы можем использовать Spring Remoting
для предоставления RPC поверх системы обмена сообщениями.
Вероятно, это не лучший способ для основных сценариев, в которых мы, вероятно, предпочитаем использовать асинхронность RabbitMQ
, но в некоторых избранных и ограниченных сценариях синхронный вызов может быть проще для понимания, быстрее и проще в разработке.
Как обычно, вы найдете исходники на GitHub .