Отправка сообщений RabbitMQ с помощью Spring AMQP

Отправка сообщений RabbitMQ с помощью Spring AMQP

1. Вступление

В этом руководстве мы рассмотрим концепциюfanout и обмен темами сSpring AMQP иRabbitMQ.

На высоком уровнеfanout exchanges будетbroadcast the same message to all bound queues, аtopic exchanges будет использовать ключ маршрутизации дляpassing messages to a particular bound queue or queues.

Для этого руководства рекомендуется предварительно прочитатьMessaging With Spring AMQP.

2. Настройка Fanout Exchange

Давайте настроим один обмен разветвлениями с привязанными к нему двумя очередями. Когда мы отправим сообщение на этот обмен, обе очереди получат сообщение. Наш разветвленный обмен игнорирует любой ключ маршрутизации, включенный в сообщение.

Spring AMQP позволяет нам агрегировать все объявления очередей, обменов и привязок в объектеDeclarables:

@Bean
public Declarables fanoutBindings() {
    Queue fanoutQueue1 = new Queue("fanout.queue1", false);
    Queue fanoutQueue2 = new Queue("fanout.queue2", false);
    FanoutExchange fanoutExchange = new FanoutExchange("fanout.exchange");

    return new Declarables(
      fanoutQueue1,
      fanoutQueue2,
      fanoutExchange,
      bind(fanoutQueue1).to(fanoutExchange),
      BindingBuilder.bind(fanoutQueue2).to(fanoutExchange));
}

3. Настройка обмена темами

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

@Bean
public Declarables topicBindings() {
    Queue topicQueue1 = new Queue(topicQueue1Name, false);
    Queue topicQueue2 = new Queue(topicQueue2Name, false);

    TopicExchange topicExchange = new TopicExchange(topicExchangeName);

    return new Declarables(
      topicQueue1,
      topicQueue2,
      topicExchange,
      BindingBuilder
        .bind(topicQueue1)
        .to(topicExchange).with("*.important.*"),
      BindingBuilder
        .bind(topicQueue2)
        .to(topicExchange).with("#.error"));
}

A topic exchange allows us to bind queues to it with different key patterns. Это очень гибкий инструмент, который позволяет нам связывать несколько очередей с одним и тем же шаблоном или даже несколько шаблонов с одной и той же очередью.

Когда ключ маршрутизации сообщения совпадает с шаблоном, оно будет помещено в очередь. If a queue has multiple bindings which match the message’s routing key, only one copy of the message is placed on the queue.с

Наши шаблоны привязки могут использовать звездочку («*»), чтобы соответствовать слову в определенной позиции, или знак фунта («#»), чтобы соответствовать нулю или большему количеству слов.

Итак, нашtopicQueue1 будет получать сообщения с ключами маршрутизации, имеющими шаблон из трех слов, где среднее слово является «важным», например:“user.important.error” или“blog.important.notification”.

И нашtopicQueue2 будет получать сообщения, ключи маршрутизации которых заканчиваются словом error; примеры соответствия:“error”,“user.important.error” или“blog.post.save.error”.

4. Настройка продюсера

Мы будем использовать методconvertAndSend дляRabbitTemplate для отправки наших примеров сообщений:

    String message = " payload is broadcast";
    return args -> {
        rabbitTemplate.convertAndSend(FANOUT_EXCHANGE_NAME, "", "fanout" + message);
        rabbitTemplate.convertAndSend(TOPIC_EXCHANGE_NAME, ROUTING_KEY_USER_IMPORTANT_WARN,
            "topic important warn" + message);
        rabbitTemplate.convertAndSend(TOPIC_EXCHANGE_NAME, ROUTING_KEY_USER_IMPORTANT_ERROR,
            "topic important error" + message);
    };

RabbitTemplate предоставляет множество перегруженных методовconvertAndSend() для различных типов обмена.

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

Когда мы отправляем сообщение в тему обмена, нам нужно передать ключ маршрутизации. На основе этого ключа маршрутизации сообщение будет доставлено в определенные очереди.

5. Конфигурирование потребителей

Наконец, давайте настроим четырех потребителей - по одному для каждой очереди - для приема созданных сообщений:

    @RabbitListener(queues = {FANOUT_QUEUE_1_NAME})
    public void receiveMessageFromFanout1(String message) {
        System.out.println("Received fanout 1 message: " + message);
    }

    @RabbitListener(queues = {FANOUT_QUEUE_2_NAME})
    public void receiveMessageFromFanout2(String message) {
        System.out.println("Received fanout 2 message: " + message);
    }

    @RabbitListener(queues = {TOPIC_QUEUE_1_NAME})
    public void receiveMessageFromTopic1(String message) {
        System.out.println("Received topic 1 (" + BINDING_PATTERN_IMPORTANT + ") message: " + message);
    }

    @RabbitListener(queues = {TOPIC_QUEUE_2_NAME})
    public void receiveMessageFromTopic2(String message) {
        System.out.println("Received topic 2 (" + BINDING_PATTERN_ERROR + ") message: " + message);
    }

We configure consumers using the @RabbitListener annotation. Здесь передается единственный аргумент - имя очереди. Потребители не знают здесь об обменах или ключах маршрутизации.

6. Запуск примера

Наш пример проекта представляет собой приложение Spring Boot, поэтому оно будет инициализировать приложение вместе с подключением к RabbitMQ и настроить все очереди, обмены и привязки.

По умолчанию наше приложение ожидает экземпляр RabbitMQ, работающий на локальном хосте через порт 5672. Мы можем изменить это и другие значения по умолчанию вapplication.yaml.

Наш проект предоставляет конечную точку HTTP на URI -/broadcast - который принимает POST с сообщением в теле запроса.

Когда мы отправляем запрос на этот URI с телом «Test», мы должны увидеть что-то похожее на это в выводе:

Received fanout 1 message: fanout payload is broadcast
Received topic 1 (*.important.*) message: topic important warn payload is broadcast
Received topic 2 (#.error) message: topic important error payload is broadcast
Received fanout 2 message: fanout payload is broadcast
Received topic 1 (*.important.*) message: topic important error payload is broadcast

Порядок, в котором мы увидим эти сообщения, конечно, не гарантируется.

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

В этом кратком руководстве мы рассмотрели разветвление и обмен темами с Spring AMQP и RabbitMQ.

Полный исходный код и все фрагменты кода для этого руководства доступны наGitHub repository.