RabbitMQ switch section 03

Posted by gigabyt3r on Thu, 04 Nov 2021 20:25:35 +0100

Function:

1. Publish subscribe model
2. Routing model
3. Theme model

1 github: source code address

2 rabbitmq02 subproject

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.yzm</groupId>
        <artifactId>rabbitmq</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
    </parent>

    <artifactId>rabbitmq02</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>rabbitmq02</name>
    <description>Demo project for Spring Boot</description>

    <dependencies>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Project structure

application.yml

spring:
  rabbitmq:
    port: 5672
    host: 127.0.0.1
    username: guest
    password: guest
    listener:
      simple:
        acknowledge-mode: auto
        prefetch: 1

3 publish and subscribe

  • In the previous article, producer production messages are sent directly to the queue for storage;
    Now it is sent to the switch, and the switch forwards the message to the queue. The switch can forward it to all queues bound to it or to queues conforming to routing rules. The switch itself will not store the message. If no queue is bound, the message will be lost
  • The switch used for publishing and subscribing is Fanout switch, also known as broadcast switch
    Broadcast switch: there is no concept of routing key in the fanout switch. It will send messages to all queues bound to the switch

Create switches, queues

package com.yzm.rabbitmq02.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class FanoutRabbitConfig {

    //queue
    public static final String QUEUE_A = "queue-a";
    public static final String QUEUE_B = "queue-b";

    public static final String FANOUT_EXCHANGE = "fanout.exchange";

    @Bean
    public Queue queueA() {
        return new Queue(QUEUE_A);
    }

    @Bean
    public Queue queueB() {
        return new Queue(QUEUE_B);
    }

    /**
     * Message switch configuration
     * Define the switch direct exchange and bind the message queue
     * name: Switch name
     * durable: Set whether it is persistent. If it is set to true, the Exchange will be saved and the data will not be lost even if the server is restarted
     * autoDelete: Set whether to delete automatically. When the last Queue bound to Exchange is deleted, the Exchange will be deleted automatically. In short, if the Exchange is not bound to any Queue, it will be deleted
     * internal: Set whether RabbitMQ is used internally. The default is false. If it is set to true, it indicates that it is a built-in switch. The client program cannot directly send messages to this switch, but can only route to the switch through the switch.
     * <p>
     * Broadcast switch: there is no concept of routing key in the fanout switch. It will send messages to all queues bound to the switch.
     * Routing switch: the direct switch is relatively simple. The matching rule is: if the routing key matches, the message will be delivered to the relevant queue
     * Topic switch: topic switch you use the principle of fuzzy matching routing keys to forward messages to the queue
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return ExchangeBuilder.fanoutExchange(FANOUT_EXCHANGE).build();
    }

    /**
     * Bind message queue to switch
     * The switch is equivalent to a Map container. The route corresponds to the key and the Queue corresponds to the value
     * When sending a message, the key in the specified switch can add the message to the corresponding queue
     */
    @Bean
    public Binding bindingA() {
        return BindingBuilder.bind(queueA()).to(fanoutExchange());
    }

    @Bean
    public Binding bindingB() {
        return BindingBuilder.bind(queueB()).to(fanoutExchange());
    }
}

producer

package com.yzm.rabbitmq02.service;

import com.yzm.rabbitmq02.config.FanoutRabbitConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class FanoutSenderService {

    private final RabbitTemplate rabbitTemplate;
    private int count = 1;

    public FanoutSenderService(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Scheduled(fixedDelay = 500, initialDelay = 10000)
    public void send() {
        if (count <= 10) {
            String message = "Hello.........." + count++;
            rabbitTemplate.convertAndSend(FanoutRabbitConfig.FANOUT_EXCHANGE, "", message);
            System.out.println(" [ producer ] Sent ==> '" + message + "'");
        }
    }
}

consumer

package com.yzm.rabbitmq02.service;

import com.yzm.rabbitmq02.config.FanoutRabbitConfig;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class FanoutReceiverService {

    private int countA = 1;
    private int countB = 1;
    private int countB_2 = 1;

    @RabbitListener(queues = FanoutRabbitConfig.QUEUE_A)
    public void receiveA(String message) {
        try {
            System.out.println(" [ consumer@A number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@A number ] Dealt with: " + countA++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = FanoutRabbitConfig.QUEUE_B)
    public void receiveB(String message) {
        try {
            System.out.println(" [ consumer@B number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@B number ] Dealt with: " + countB++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = FanoutRabbitConfig.QUEUE_B)
    public void receiveB_2(String message) {
        try {
            System.out.println(" [ consumer@B_2 number ] Received ==> '" + message + "'");
            Thread.sleep(2000);
            System.out.println(" [ consumer@B_2 number ] Dealt with: " + countB_2++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Start project

The operation results are as follows

The producer produces 10 messages and sends them to the switch, which forwards the messages to the queues A and B bound to it respectively;
Queues A and B get 10 messages with the same content. Queue A has only one consumer A, so consumer A processes all messages
Queue B has two consumers B, B_2. The distribution mode is based on capacity, so consumer B handles more

4 routing model

Routing switch: the direct switch is relatively simple. The matching rule is: if the routing key matches completely, the message will be delivered to the relevant queue

Create switches, queues

package com.yzm.rabbitmq02.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class DirectRabbitConfig {

    //queue
    public static final String QUEUE_C = "queue-c";
    public static final String QUEUE_D = "queue-d";

    //Routing key
    public static final String DIRECT_C = "direct.c";
    public static final String DIRECT_D = "direct.d";
    public static final String DIRECT_D2 = "direct.dcd";

    public static final String DIRECT_EXCHANGE = "direct.exchange";

    @Bean
    public Queue queueC() {
        return new Queue(QUEUE_C);
    }

    @Bean
    public Queue queueD() {
        return new Queue(QUEUE_D);
    }


    /**
     * Message switch configuration
     * Define the switch direct exchange and bind the message queue
     * name: Switch name
     * durable: Set whether it is persistent. If it is set to true, the Exchange will be saved and the data will not be lost even if the server is restarted
     * autoDelete: Set whether to delete automatically. When the last Queue bound to Exchange is deleted, the Exchange will be deleted automatically. In short, if the Exchange is not bound to any Queue, it will be deleted
     * internal: Set whether RabbitMQ is used internally. The default is false. If it is set to true, it indicates that it is a built-in switch. The client program cannot directly send messages to this switch, but can only route to the switch through the switch.
     * <p>
     * Broadcast switch: there is no concept of routing key in the fanout switch. It will send messages to all queues bound to the switch.
     * Routing switch: the direct switch is relatively simple. The matching rule is: if the routing key matches completely, the message will be delivered to the relevant queue
     * Topic switch: topic switch you use the principle of fuzzy matching routing keys to forward messages to the queue
     */
    @Bean
    public DirectExchange directExchange() {
        return ExchangeBuilder.directExchange(DIRECT_EXCHANGE).build();
    }

    /**
     * Bind message queue to switch
     * The switch is equivalent to a Map container. The route corresponds to the key and the Queue corresponds to the value
     * When sending a message, the key in the specified switch can add the message to the corresponding queue
     */
    @Bean
    public Binding bindingC() {
        return BindingBuilder.bind(queueC()).to(directExchange()).with(DIRECT_C);
    }

    @Bean
    public Binding bindingD() {
        return BindingBuilder.bind(queueD()).to(directExchange()).with(DIRECT_D);
    }

    @Bean
    public Binding bindingD2() {
        return BindingBuilder.bind(queueD()).to(directExchange()).with(DIRECT_D2);
    }

}

producer

package com.yzm.rabbitmq02.service;

import com.yzm.rabbitmq02.config.DirectRabbitConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class DirectSenderService {

    private final RabbitTemplate rabbitTemplate;

    private int count = 0;

    public DirectSenderService(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Scheduled(fixedDelay = 500, initialDelay = 10000)
    public void send_1() {
        count++;
        if (count <= 30) {
            String message = "Hello.........." + count;
            System.out.println(" [ producer ] Sent ==> '" + message + "'");
            if (count % 3 == 0) {
                rabbitTemplate.convertAndSend(DirectRabbitConfig.DIRECT_EXCHANGE, DirectRabbitConfig.DIRECT_C, message);
            } else if (count % 3 == 1) {
                rabbitTemplate.convertAndSend(DirectRabbitConfig.DIRECT_EXCHANGE, DirectRabbitConfig.DIRECT_D, message);
            } else {
                rabbitTemplate.convertAndSend(DirectRabbitConfig.DIRECT_EXCHANGE, DirectRabbitConfig.DIRECT_D2, message);
            }
        }
    }

}

consumer

package com.yzm.rabbitmq02.service;

import com.yzm.rabbitmq02.config.DirectRabbitConfig;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class DirectReceiverService {

    private int countC = 1;
    private int countD = 1;
    private int countD_2 = 1;

    @RabbitListener(queues = DirectRabbitConfig.QUEUE_C)
    public void receiveC(String message) {
        try {
            System.out.println(" [ consumer@C number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@C number ] Dealt with: " + countC++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = DirectRabbitConfig.QUEUE_D)
    public void receiveD(String message) {
        try {
            System.out.println(" [ consumer@D number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@D number ] Dealt with: " + countD++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = DirectRabbitConfig.QUEUE_D)
    public void receiveD_2(String message) {
        try {
            System.out.println(" [ consumer@D_2 number ] Received ==> '" + message + "'");
            Thread.sleep(2000);
            System.out.println(" [ consumer@D_2 number ] Dealt with: " + countD_2++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In order not to affect the test of routing mode, turn off the timer of publish subscribe mode

Start project

The operation results are as follows:

We created two queues C and D, C has one routing key and D has two routing keys;
The producer produces 30 messages, which are sent to the switch evenly through the routing key. The switch allocates 10 messages to queue C and 20 messages to queue D according to the routing key;
From the results of operation, the expected results are indeed achieved

5 theme mode (wildcard mode)

Topic switch: topic switch uses the principle of fuzzy matching routing key to forward messages to the queue

Create switches, queues

package com.yzm.rabbitmq02.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class TopicRabbitConfig {

    //queue
    public static final String QUEUE_E = "queue-e";
    public static final String QUEUE_F = "queue-f";
    public static final String QUEUE_G = "queue-g";

    public static final String TOPIC_EXCHANGE = "topic.exchange";

    //Two special characters * and # are used for fuzzy matching, where * is used to match one word and # is used to match multiple words (can be zero)
    public static final String TOPIC_E = "topic.*";
    public static final String TOPIC_F = "topic.#";
    public static final String TOPIC_G = "topic.*.key";

    @Bean
    public Queue queueE() {
        return new Queue(QUEUE_E);
    }

    @Bean
    public Queue queueF() {
        return new Queue(QUEUE_F);
    }

    @Bean
    public Queue queueG() {
        return new Queue(QUEUE_G);
    }


    /**
     * Message switch configuration
     * Define the switch direct exchange and bind the message queue
     * name: Switch name
     * durable: Set whether it is persistent. If it is set to true, the Exchange will be saved and the data will not be lost even if the server is restarted
     * autoDelete: Set whether to delete automatically. When the last Queue bound to Exchange is deleted, the Exchange will be deleted automatically. In short, if the Exchange is not bound to any Queue, it will be deleted
     * internal: Set whether RabbitMQ is used internally. The default is false. If it is set to true, it indicates that it is a built-in switch. The client program cannot directly send messages to this switch, but can only route to the switch through the switch.
     * <p>
     * Broadcast switch: there is no concept of routing key in the fanout switch. It will send messages to all queues bound to the switch.
     * Routing switch: the direct switch is relatively simple. The matching rule is: if the routing key matches, the message will be delivered to the relevant queue
     * Topic switch: topic switch uses the principle of fuzzy matching routing key to forward messages to the queue
     */
    @Bean
    public TopicExchange topicExchange() {
        return ExchangeBuilder.topicExchange(TOPIC_EXCHANGE).build();
    }

    /**
     * Bind message queue to switch
     * The switch is equivalent to a Map container. The route corresponds to the key and the Queue corresponds to the value
     * When sending a message, the key in the specified switch can add the message to the corresponding queue
     */
    @Bean
    public Binding bindingE() {
        return BindingBuilder.bind(queueE()).to(topicExchange()).with(TOPIC_E);
    }

    @Bean
    public Binding bindingF() {
        return BindingBuilder.bind(queueF()).to(topicExchange()).with(TOPIC_F);
    }

    @Bean
    public Binding bindingG() {
        return BindingBuilder.bind(queueG()).to(topicExchange()).with(TOPIC_G);
    }
}

producer

package com.yzm.rabbitmq02.service;

import com.yzm.rabbitmq02.config.TopicRabbitConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class TopicSenderService {

    private final RabbitTemplate rabbitTemplate;
    private int count = 0;

    public TopicSenderService(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Scheduled(fixedDelay = 500, initialDelay = 10000)
    public void send_1() {
        count++;
        if (count <= 30) {
            String message = "Hello.........." + count;
            System.out.println(" [ producer ] Sent ==> '" + message + "'");
            if (count % 3 == 0) {
                rabbitTemplate.convertAndSend(TopicRabbitConfig.TOPIC_EXCHANGE, "topic.yzm", message);
            } else if (count % 3 == 1) {
                rabbitTemplate.convertAndSend(TopicRabbitConfig.TOPIC_EXCHANGE, "topic.yzm.yzm", message);
            } else {
                rabbitTemplate.convertAndSend(TopicRabbitConfig.TOPIC_EXCHANGE, "topic.yzm.key", message);
            }
        }
    }

}

consumer

package com.yzm.rabbitmq02.service;

import com.yzm.rabbitmq02.config.TopicRabbitConfig;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class TopicReceiverService {

    private int countE = 1;
    private int countF = 1;
    private int countG = 1;

    @RabbitListener(queues = TopicRabbitConfig.QUEUE_E)
    public void receiveE(String message) {
        try {
            System.out.println(" [ consumer@E number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@E number ] Dealt with: " + countE++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = TopicRabbitConfig.QUEUE_F)
    public void receiveF(String message) {
        try {
            System.out.println(" [ consumer@F number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@F number ] Dealt with: " + countF++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = TopicRabbitConfig.QUEUE_G)
    public void receiveG(String message) {
        try {
            System.out.println(" [ consumer@G number ] Received ==> '" + message + "'");
            Thread.sleep(1000);
            System.out.println(" [ consumer@G number ] Dealt with: " + countG++);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Turn off the front timer

Start project

The operation results are as follows:

Analyze the number of messages received by E, F and G queues
The operation results are consistent with the expected results

Topics: RabbitMQ Spring Boot