Spring in Action Covers Spring 5-1--11 — страница 41 из 63

Рис. 8.1. Сообщения, отправляемые на обмен RabbitMQ, направляются в одну или несколько очередей на основе ключей маршрутизации и привязок.

Когда сообщение приходит к брокеру RabbitMQ, оно отправляется на тот обмен (exchange), для которого оно было адресовано. Exchange отвечает за маршрутизацию сообщения в одну или несколько очередей в зависимости от типа exchange, привязки между exchange и очередями (queue) и значения ключа маршрутизации сообщения.

Существует несколько видов обмена, включая следующие:

Default — обмен, который автоматически создается брокером. Он направляет сообщения в очереди, чье имя совпадает с ключом маршрутизации сообщения. Все очереди по умолчанию привязаны к default exchange.

Direct - направляет сообщения в очередь, ключ привязки которой совпадает с ключом маршрутизации сообщения.

Topic - направляет сообщение в одну или несколько очередей, в которых ключ привязки (который может содержать символы подстановки) соответствует ключу маршрутизации сообщения.

Fanout - маршрутизирует сообщения во все связанные очереди без учета ключей привязки или ключей маршрутизации.

Headers - аналогичны обмену topic, за исключением того, что маршрутизация основана на значениях заголовков сообщений, а не на ключах маршрутизации.

Dead letter - универсальное сообщение для всех сообщений, которые невозможно доставить (это означает, что они не соответствуют какой-либо определенной привязке обмена к очереди).

Простейшие формы обмена это Default и Fanout, так как они примерно соответствуют очереди и теме JMS. Но другие типы обмена позволяют задать более гибкие схемы маршрутизации.

Самая важная вещь для понимания - это то, что сообщения отправляются на обменники (exchanges) с ключами маршрутизации, и они берутся из очередей. То, как они попадают из обмена в очередь, зависит от определений привязки и того, что лучше всего подходит для ваших сценариев использования.

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

П р и м е ч а н и е - Более подробное обсуждение того, как лучше связать очереди с обменниками , см. RabbitMQ in Action Alvaro Videla and Jason J.W. Williams (Manning, 2012).

8.2.1 Добавление RabbitMQ в Spring

Прежде чем вы сможете начать отправку и получение сообщений RabbitMQ с помощью Spring, вам нужно добавить в свою сборку зависимость AMQP-стартера Spring Boot вместо Artemis или ActiveMQ-стартера, которые вы добавили в предыдущем разделе:

org.springframework.boot

spring-boot-starter-amqp

Добавление стартера AMQP в вашу сборку вызовет автоконфигурацию, которая создаст фабрику соединений AMQP и компоненты RabbitTemplate, а также другие вспомогательные компоненты. Просто добавьте эту зависимость, чтобы начать отправку и получение сообщений от брокера RabbitMQ с помощью Spring. Но есть несколько полезных свойств, о которых вы захотите узнать, перечисленных в таблице 8.4.

Таблица 8.4 Свойства для настройки местоположения и учетных данных RabbitMQ брокера

spring.rabbitmq.addresses - Разделенный запятыми список адресов брокера RabbitMQ

spring.rabbitmq.host - Хост брокера (по умолчанию localhost)

spring.rabbitmq.port - Порт брокера (по умолчанию 5672)

spring.rabbitmq.username - Имя пользователя для доступа к брокеру (необязательно)

spring.rabbitmq.password - Пароль пользователя для доступа к брокеру (необязательно)

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

Например, предположим, что при переходе в продакшен ваш брокер RabbitMQ находится на сервере с именем rabbit.tacocloud.com, прослушивает порт 5673 и требует учетных данных. В этом случае следующая конфигурация в вашем файле application.yml установит эти свойства, когда профиль prod активен:

spring:

    profiles: prod

    rabbitmq:

        host: rabbit.tacocloud.com

       port: 5673

      username: tacoweb

      password: l3tm31n

Теперь, когда RabbitMQ настроен в вашем приложении, пришло время начать отправку сообщений с RabbitTemplate.

8.2.2 Отправка сообщений с RabbitTemplate

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

Что касается отправки сообщений с помощью RabbitTemplate, методы send() и convertAndSend() аналогичны методам с такими же именами из JmsTemplate. Но в отличие от методов JmsTemplate, которые направляют сообщения только в определенную очередь или тему, методы RabbitTemplate отправляют сообщения в  с точки зрения обмена и ключей маршрутизации. Вот несколько наиболее важных методов отправки сообщений с RabbitTemplate (эти методы определены в AmqpTemplate, интерфейсе, реализованном RabbitTemplate.):

// Отправка необработанных сообщений

void send(Message message) throws AmqpException;

void send(String routingKey, Message message) throws AmqpException;

void send(String exchange, String routingKey, Message message) throws AmqpException;

// Отправлять сообщения, преобразованные из объектов

void convertAndSend(Object message) throws AmqpException;

void convertAndSend(String routingKey, Object message) throws AmqpException;

void convertAndSend(String exchange, String routingKey, Object message) throws AmqpException;

// Отправка сообщений, преобразованных из объектов с последующей обработкой (post-processing)

void convertAndSend(Object message, MessagePostProcessor mPP) throws AmqpException;

void convertAndSend(String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException;

void convertAndSend(String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException;

Как видите, эти методы следуют шаблону, аналогичному их близнецам в JmsTemplate. Первые три метода send() отправляют необработанный объект Message. Следующие три метода convertAndSend() принимают объект, который будет преобразован в Message за кулисами перед отправкой. Последние три метода convertAndSend() похожи на предыдущие три, но они принимают MessagePostProcessor, который можно использовать для манипулирования объектом Message до его отправки брокеру.

Эти методы отличаются от своих аналогов JmsTemplate тем, что они принимают значения String, чтобы указать ключ обмена и маршрутизации, а не имя назначения (или Destination назначения). Методы, которые не принимают exchange, будут отправлять свои сообщения в exchange по умолчанию. Аналогично, методы, которые не принимают ключ маршрутизации, будут маршрутизировать свои сообщения с ключом маршрутизации по умолчанию.

Давайте включим RabbitTemplate для работы с отправкой тако-заказов. Один из способов сделать это - использовать метод send(), как показано в листинге 8.5. Но прежде чем вы сможете вызвать send(), вам нужно преобразовать объект Order в a Message. Это может быть утомительной работой, если бы не тот факт, что RabbitTemplate делает свой конвертер сообщений доступным с помощью метода getMessageConverter().

Листинг 8.5 Отправка сообщений с RabbitTemplate.send()

package tacos.messaging;

import org.springframework.amqp.core.Message;

import org.springframework.amqp.core.MessageProperties;

import org.springframework.amqp.rabbit.core.RabbitTemplate;

import org.springframework.amqp.support.converter.MessageConverter;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import tacos.Order;

@Service

public class RabbitOrderMessagingService implements OrderMessagingService {

    private RabbitTemplate rabbit;

    @Autowired

    public RabbitOrderMessagingService(RabbitTemplate rabbit) {

        this.rabbit = rabbit;

    }

    public void sendOrder(Order order) {

        MessageConverter converter = rabbit.getMessageConverter();

        MessageProperties props = new MessageProperties();

        Message message = converter.toMessage(order, props);

       rabbit.send("tacocloud.order", message);

}

}

Если у вас есть MessageConverter, конвертировать Order в Message становится просто. Вы должны задать любые свойства сообщения с MessageProperties, но если вам не нужно устанавливать какие-либо такие свойства, то подойдет экземпляр MessageProperties по умолчанию. Затем все, что осталось - это вызвать send(), передав ключ обмена и маршрутизации (оба из которых являются необязательными) вместе с сообщением. В этом примере вы указываете только ключ маршрутизации - tacocloud.order - вместе с сообщением, поэтому будет использоваться exchange (обмен) по умолчанию.

Говоря об default exchange, именем обмена по умолчанию является «» (пустая строка), что соответствует default exchange, автоматически создаваемому брокером RabbitMQ. Точно так же ключом маршрутизации по умолчанию является «» (маршрутизация которого зависит от рассматриваемого обмена и привязок (exchange and binding)). Вы можете переопределить эти значения по умолчанию, установив свойства spring.rabbitmq.template.exchange и spring.rabbitmq.template.routing-key: