RabbitMQ message queue

Keywords: RabbitMQ Spring Boot

1, Introduction

Message queue functions: decoupling, asynchronous and peak clipping

Why use message queuing? What are the benefits of messages for columns- Laughing Terry - blog Gardenhttps://www.cnblogs.com/terry-love/p/11492397.html

2, RabbitMQ introduction

Several popular MQ models in the market:

ActiveMQ,RocketMQ,Kafka,RabbitMQ.

  • Language support: ActiveMQ, RocketMQ only support Java language, Kafka can support multiple languages, and RabbitMQ supports multiple languages.

  • In terms of efficiency: ActiveMQ, RocketMQ and Kafka are all at the millisecond level, and RabbitMQ is at the microsecond level.

  • Message loss and message duplication: RabbitMQ has mature solutions for message persistence and duplication.

  • Learning cost: RabbitMQ is very simple.

RabbitMQ is developed and maintained by Rabbit company, and finally in pivot.

RabbitMQ strictly follows AMQP protocol and advanced message queuing protocol to help us deliver asynchronous messages between processes.

3, RabbitMQ installation (for example, Alibaba cloud server deployment)

First, check whether there are running containers in docker and whether the port number is occupied

Next, create a folder in which you create the docker-compose.yaml file

The configuration in the ymal file is as follows
 

version: "3.1"
services:
  rabbitmq:
   image: daocloud.io/library/rabbitmq:management
   restart: always
   container_name: rabbitmq
   ports:
     - 5672:5672
     - 15672:15672
   volumes:
     - ./data:/var/lib/rabbitmq

Start (it is recommended to start at the front desk for the first time)

docker-compose up

Log in (note that there is a compatibility problem with Google browser, use IE)

be careful! My alicloud server ip is 8.130.166.101. Please change it according to your own ip address

The following 15672 is the port number

http://8.130.166.101:15672/http://8.130.166.101:15672/

User name guest password guest

After entering, you can come in

 

4, RabbitMQ Architecture [key]

4.1 official simple structure diagram

  • Publisher - Producer: publishes messages to Exchange in RabbitMQ

  • Consumer - Consumer: listens for messages in the Queue in RabbitMQ

  • Exchange - switch: establishes a connection with the producer and receives messages from the producer

  • Queue - queue: Exchange will distribute messages to the specified queue, which interacts with consumers

  • Routes - routes: what policy does the switch use to publish messages to the Queue

Simple architecture diagram

4.2 complete architecture diagram of rabbitmq

Complete architecture diagram

Complete architecture diagram

5, Use of RabbitMQ [key]

5.1 RabbitMQ communication mode

Communication mode

 

 

 

 

 

5.2 Java connection RabbitMQ

5.2.1 create maven project

5.2.2 import dependency

<dependencies>
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>5.6.0</version>
    </dependency>
​
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

5.2.3 create tool class connection RabbitMQ

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class RabbitMqUtils {

    public static Connection getConnection() throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("8.130.166.101");
        connectionFactory.setPort(5672);
        //Set login user name and password
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //Where is it stored
        connectionFactory.setVirtualHost("/");
        return connectionFactory.newConnection();
    }

}

 

5.3 Hello-World

A producer, a default switch, a queue, and a consumer

Structure diagram

 

Create a producer, create a channel, publish messages to exchange, and specify routing rules.

@Test
    public void Publisher() throws IOException, TimeoutException {
        Channel channel = connection.createChannel();
        // Parameter 1: specify exchange and use ''.
        // Parameter 2: specify the routing rule and use the specific queue name.
        // Parameter 3: specify the properties carried by the delivered message and use null.
        // Parameter 4: Specifies the specific message to be published, byte [] type

        // Send the message "Hello queue" to the queue
        channel.basicPublish("","hello-queue",null,"hello-queue".getBytes());
        channel.close();
    }

    @After
    public void destroy() throws IOException {
        connection.close();
    }

Create a consumer, create a channel, create a queue, and consume the current queue

/*
    * consumer*/
    @Test
    public void consumerTest() throws IOException {
        //Create pipe
        Channel channel = connection.createChannel();
        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        //channel and queue binding
        channel.queueDeclare("hello-queue",true,true,false,null);

        //Hold one piece of data every time consumers consume
        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Get information from queue
                System.out.println("Accept message:"+new String(body,"utf-8"));
            }
        };

        // channel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
        // Parameter 3 Consumer

        channel.basicConsume("hello-queue",true,consumer);

        //Get the program stuck
        System.in.read();

    }

Run twice

  Messages can be obtained from consumers

Manual ack mechanism

Manual ack mechanism: ensure that the service corresponding to the message has been actually processed, rather than just receiving the message

 

 /*
     * consumer*/
    @Test
    public void consumerTest() throws IOException {
        //Create pipe
        Channel channel = connection.createChannel();
        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        //channel and queue binding
        channel.queueDeclare("hello-queue",true,true,false,null);

        //Hold one piece of data every time consumers consume
        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Get information from queue
                System.out.println("Accept message:"+new String(body,"utf-8"));
                // After all services are completed, you can manually ack
                // envelope.getDeliveryTag() / / message tag 0 1 2
                // False do not delete after ACK
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        // channel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
        // Parameter 3 Consumer

        channel.basicConsume("hello-queue",false,consumer);

        //Get the program stuck
        System.in.read();

    }

 

5.4 Work

One producer, one default switch, one queue, two consumers

Structure diagram

 

On the consumer side, you only need to add Qos capabilities and change to manual ack to allow consumers to consume the specified messages according to their capabilities, instead of being evenly distributed by RabbitMQ by default, the producer remains unchanged, and normally publish messages to the default exchange and specify routing

Consumer specifies Qoa and manual ack

Two consumers consume the same message queue

  
public class WorkerQueueTest {
    private Connection connection;


    @Before
    public void init() throws IOException, TimeoutException {
        connection = RabbitMqUtils.getConnection();
    }

    /*
     * consumer*/
    @Test
    public void consumer1Test() throws IOException {
        //Create pipe
        Channel channel = connection.createChannel();
        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        //channel and queue binding
        channel.queueDeclare("work-queue",true,false,false,null);

        //Hold one piece of data every time consumers consume
        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Get information from queue
                System.out.println("Accept message:"+new String(body,"utf-8"));
            }
        };

        // channel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
        // Parameter 3 Consumer

        channel.basicConsume("work-queue",true,consumer);

        //Get the program stuck
        System.in.read();

    }

    @Test
    public void consumer2Test() throws IOException {
        //Create pipe
        Channel channel = connection.createChannel();
        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        //channel and queue binding
        channel.queueDeclare("work-queue",true,false,false,null);

        //Hold one piece of data every time consumers consume
        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Get information from queue
                System.out.println("Accept message:"+new String(body,"utf-8"));
            }
        };

        // channel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
        // Parameter 3 Consumer

        channel.basicConsume("work-queue",true,consumer);

        //Get the program stuck
        System.in.read();

    }


    @Test
    public void Publisher() throws IOException, TimeoutException {
        Channel channel = connection.createChannel();
        //Parameter 1: if the switch name does not exist, it is the default ''
        // Parameter 2: match the queue name or message type information with the routing rules
        // Parameter 3: specify the properties carried by the delivered message and use null.
        // Parameter 4: message

        // Send ten messages "Hello queue" to the queue
        for (int i = 0; i < 10; i++) {
            channel.basicPublish("","work-queue",null,("work-queue"+i).getBytes());
        }
        channel.close();
    }

    @After
    public void destroy() throws IOException {
        connection.close();
    }
}

Run two consumers first, and then one producer

You can see the results as follows


 

 

The following must first start consumers before starting producers

5.5 Publish/Subscribe

One producer, one switch, two queues, two consumers

Structure diagram

 

Declare an exchange of Fanout type and bind exchange and queue together. The binding method is direct binding.

Let the producer create an exchange and specify the type to bind to one or more queues.

public class PublishSubTest {
    private Connection connection;


    @Before // Call initialization data before @Test
    public void init() throws IOException, TimeoutException {

        connection = RabbitMqUtils.getConnection();
    }


    /**
     * When testing, be sure to start the consumer first and start the producer later
     */
    @Test // Conduct unit tests
    public void consumer1Test() throws IOException {

        // channel pipes connect consumers and queues
        final Channel channel = connection.createChannel();

        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        // chanel and queue binding
        channel.queueDeclare("pubsub-queue1",true,false,false,null);

        // Hold one piece of data every time consumers consume
        channel.basicQos(1);


        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                // Get the message from the queue and the processor
                System.out.println("Consumer 1 received message:"+new String(body,"utf-8") );



                // After all services are completed, you can manually ack
                // envelope.getDeliveryTag() / / message tag 0 1 2
                // False do not delete after ACK
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        // chanel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
//                  false requires manual ack
        // Parameter 3 Consumer
        channel.basicConsume("pubsub-queue1", false,consumer);


        // Let the program consistent card where consumers can consistently consume messages
        System.in.read();// Wait for input from the client command line

    }

    @Test // Conduct unit tests
    public void consumer2Test() throws IOException {

        // channel pipes connect consumers and queues
        final Channel channel = connection.createChannel();

        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        // chanel and queue binding
        channel.queueDeclare("pubsub-queue2",true,false,false,null);

        // Hold one piece of data every time consumers consume
        channel.basicQos(1);


        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                // Get the message from the queue and the processor
                System.out.println("Consumer 2 received message:"+new String(body,"utf-8") );



                // After all services are completed, you can manually ack
                // envelope.getDeliveryTag() / / message tag 0 1 2
                // False do not delete after ACK
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        // chanel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
//                  false requires manual ack
        // Parameter 3 Consumer
        channel.basicConsume("pubsub-queue2", false,consumer);


        // Let the program consistent card where consumers can consistently consume messages
        System.in.read();// Wait for input from the client command line

    }

    /**
     * Producer model
     */
    @Test
    public void publishTest() throws IOException, TimeoutException {

        Channel channel = connection.createChannel();


        //Bind the change to the custom switch "PubSub exchange"
        //Parameter 1: name of exchange
        //Parameter 2: specify the type of exchange fanout - PubSub, direct - routing, topic - topics
        //FANOUT - pubsub exchange sends messages to all queues
        channel.exchangeDeclare("pubsub-exchange", BuiltinExchangeType.FANOUT);

        //Parameter 1 queue name
        //Parameter 2 switch name
        //Parameter 3 routing rules
        channel.queueBind("pubsub-queue1", "pubsub-exchange", "");
        channel.queueBind("pubsub-queue2", "pubsub-exchange", "");

        //Parameter 1: if the switch name does not exist, it is the default ''
        // Parameter 2: the queue name or message type information will really match the routing rules
        // Parameter 3: specify the properties carried by the delivered message and use null.
        // Parameter 4: message

        // Send message to queue

        // Send 10 pieces of data and each consumer gets 5 pieces of data
        for (int i = 0; i < 10; i++) {

            // The message is not sent to the default switch, but to the custom switch PubSub exchange
            // "PubSub exchange" switch name
            // '' routing rule
            channel.basicPublish("pubsub-exchange", "",null,("pubsub--i:" +i).getBytes());

        }


        channel.close();

    }



    @After// After @ Test, data is destroyed
    public void destroy() throws IOException {

        connection.close();
    }
}

Consumers can listen to a queue normally.

5.6 Routing

The following must first start consumers before starting producers

One producer, one switch, two queues, two consumers

Structure diagram

 

After creating an exchange of DIRECT type, the producer binds the corresponding queue according to the RoutingKey, and specifies the specific RoutingKey of the message when sending the message.

 
 
 

Consumers have not changed

public class RoutingTest {

    private Connection connection;

    @Before
    public void getConnection() throws IOException, TimeoutException {
        connection = RabbitMqUtils.getConnection();
    }

    @Test
    public void consumer1Test() throws IOException {
        final Channel channel = connection.createChannel();

        channel.queueDeclare("routing-info-queue",true,false,false,null);

        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("routing-info-queue Message received"+new String(body,"utf-8"));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        // chanel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
//                  false requires manual ack
        // Parameter 3 Consumer
        channel.basicConsume("routing-info-queue",false,consumer);

        System.in.read();
    }

    @Test
    public void  consumer2Test() throws IOException {
        final Channel channel = connection.createChannel();
        channel.queueDeclare("routing-error-queue",true,false,false,null);
        channel.basicQos(1);
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("routing-error-queue Accept message:"+new String(body,"utf-8"));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume("routing-error-queue",false,consumer);
        System.in.read();
    }

    @Test
    public void publisherTest() throws IOException, TimeoutException {
        Channel channel = connection.createChannel();
        //Bind the channel to the custom switch "routing exchange"
        //Parameter 1: name of exchange
        //Parameter 2: specify the type of exchange fanout - PubSub, direct - routing, topic - topics
        // DIRECT - Routing exchange sends messages to all queues
        channel.exchangeDeclare("Routing-exchange",BuiltinExchangeType.DIRECT);
        //Parameter 1 queue name
        //Parameter 2 switch name
        //Parameter 3 routing rules
        // All messages with info will be sent to the routing info queue by routing exchange
        channel.queueBind("routing-info-queue","Routing-exchange","info");
        channel.queueBind("routing-error-queue","Routing-exchange","error");
        //Parameter 1: if the switch name does not exist, it is the default ''
        // Parameter 2: the queue name or message type information will really match the routing rules
        // Parameter 3: specify the properties carried by the delivered message and use null.
        // Parameter 4: message

        // Send message to queue
        // Send 10 pieces of data and each consumer gets 5 pieces of data
        for (int i = 0; i < 10; i++) {
            if (i%2==1){//Odd number
                channel.basicPublish("Routing-exchange","info",null,("Routing--i="+i).getBytes());
            }else {

                channel.basicPublish("Routing-exchange","error",null,("Routing--i="+i).getBytes());
            }
        }
        channel.close();
    }

    @After
    public void destroy() throws IOException {
        connection.close();
    }
}

Operation results

 

 

5.7 Topic

The following must first start consumers before starting producers

One producer, one switch, two queues, two consumers

Structure diagram

 

The producer creates the exchange of the Topic and binds it to the queue. This binding can use the * and # keywords to specify the RoutingKey content. When writing, pay attention to the format xxx.xxx.xxx, * - > one XXX, and # - > represents multiple xxx.xxx. When sending messages, specify the specific RoutingKey.

Take a chestnut

//2. Create exchange and specify the binding method
channel.exchangeDeclare("topic-exchange", BuiltinExchangeType.TOPIC);
channel.queueBind("topic-queue-1","topic-exchange","*.red.*");
channel.queueBind("topic-queue-2","topic-exchange","fast.#");
channel.queueBind("topic-queue-2","topic-exchange","*.*.rabbit");
​
//3. Publish messages to exchange and specify routing rules
channel.basicPublish("topic-exchange","fast.red.monkey",null,"Red quick monkey".getBytes());
channel.basicPublish("topic-exchange","slow.black.dog",null,"Black dog".getBytes());
channel.basicPublish("topic-exchange","fast.white.cat",null,"Quick white cat".getBytes());

Consumers just listen to the queue, no change.

public class TopicTest {

    private Connection connection;


    @Before // Call initialization data before @Test
    public void init() throws IOException, TimeoutException {

        connection = RabbitMqUtils.getConnection();
    }


    /**
     * When testing, be sure to start the consumer first and start the producer later
     */
    @Test // Conduct unit tests
    public void consumer1Test() throws IOException {

        // channel pipes connect consumers and queues
        final Channel channel = connection.createChannel();

        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        // chanel and queue binding
        channel.queueDeclare("topic-queue-1",true,false,false,null);

        // Hold one piece of data every time consumers consume
        channel.basicQos(1);


        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                // Get the message from the queue and the processor
                System.out.println("Consumer 1 received message:"+new String(body,"utf-8") );



                // After all services are completed, you can manually ack
                // envelope.getDeliveryTag() / / message tag 0 1 2
                // False do not delete after ACK
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        // chanel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
//                  false requires manual ack
        // Parameter 3 Consumer
        channel.basicConsume("topic-queue-1", false,consumer);


        // Let the program consistent card where consumers can consistently consume messages
        System.in.read();// Wait for input from the client command line

    }

    @Test // Conduct unit tests
    public void consumer2Test() throws IOException {

        // channel pipes connect consumers and queues
        final Channel channel = connection.createChannel();

        //Parameter 1: queue - specifies the name of the queue
        //Parameter 2: durable - whether the current queue needs persistence (true)
        //Parameter 3: exclusive - exclusive (conn.close() - the current queue will be automatically deleted, and the current queue can only be consumed by one consumer)
        //Parameter 4: autoDelete - if there are no consumers in this queue, the queue will be deleted automatically
        //Parameter 5: arguments - specifies additional information about the current queue

        // chanel and queue binding
        channel.queueDeclare("topic-queue-2",true,false,false,null);

        // Hold one piece of data every time consumers consume
        channel.basicQos(1);


        Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                // Get the message from the queue and the processor
                System.out.println("Consumer 2 received message:"+new String(body,"utf-8") );



                // After all services are completed, you can manually ack
                // envelope.getDeliveryTag() / / message tag 0 1 2
                // False do not delete after ACK
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        // chanel and consumer binding
        // Parameter 1 queue name
        // Parameter 2 auto ack means that the consumer sends a confirmation message to the queue, otherwise the queue will send it again
//                  false requires manual ack
        // Parameter 3 Consumer
        channel.basicConsume("topic-queue-2", false,consumer);


        // Let the program consistent card where consumers can consistently consume messages
        System.in.read();// Wait for input from the client command line

    }

    /**
     * Producer model
     */
    @Test
    public void publishTest() throws IOException, TimeoutException {

        Channel channel = connection.createChannel();


        //Bind the change to the custom switch "PubSub exchange"
        //Parameter 1: name of exchange
        //Parameter 2: specify the type of exchange fanout - PubSub, direct - routing, topic - topics
        // DIRECT - Routing exchange sends messages to all queues
        channel.exchangeDeclare("topic-exchange", BuiltinExchangeType.TOPIC);

        //Parameter 1 queue name
        //Parameter 2 switch name
        //Parameter 3 routing rules
        // All messages with info will be sent to the topic-queue-1 queue by routing exchange
        channel.queueBind("topic-queue-1", "topic-exchange", "*.orange.*");
        channel.queueBind("topic-queue-2", "topic-exchange", "big.*.*");

        //Parameter 1: if the switch name does not exist, it is the default ''
        // Parameter 2: the queue name or message type information will really match the routing rules
        // Parameter 3: specify the properties carried by the delivered message and use null.
        // Parameter 4: message

        // Send message to queue
        // Send 10 pieces of data and each consumer gets 5 pieces of data
        for (int i = 0; i < 10; i++) {

            // The message is not sent to the default switch, but to the custom switch PubSub exchange
            //Parameter 1: "PubSub exchange" switch name
            //Parameter 2: '' routing rule

            if (i%2==1){// Odd orange
                channel.basicPublish("topic-exchange", "xxxasdasd.orange.xfsdf",null,("topic--i:" +i).getBytes());

            }else{ // Even error
                channel.basicPublish("topic-exchange", "big.xxxx.uii",null,("routing--i:" +i).getBytes());

            }

        }


        channel.close();

    }



    @After// After @ Test, data is destroyed
    public void destroy() throws IOException {

        connection.close();
    }

}

Operation results:

 

 

6, RabbitMQ integrates SpringBoot [key]

6.1 SpringBoot integration RabbitMQ

6.1.1 create SpringBoot project

6.1.2 import dependency

  
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
​
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <!--<scope>test</scope>-->
        </dependency>
​
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

6.1.3 writing configuration files in application.properties

spring.rabbitmq.host=8.130.166.101
spring.rabbitmq.port=5672

spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

spring.rabbitmq.virtual-host=/

6.1.4 declare exchange and queue

@Configuration
public class RabbitConfig {
    @Bean
    public TopicExchange topicExchange(){
        TopicExchange topicExchange = new TopicExchange("springboot-topic-exchange", true, false);
        return topicExchange;
    }

    @Bean
    public Queue queue(){
        Queue queue = new Queue("springboot-queue", true, false, false, null);
        return queue;
    }

    @Bean
    public Binding binding(TopicExchange topicExchange,Queue queue){
        Binding binding = BindingBuilder.bind(queue).to(topicExchange).with("*.java.*");
        return binding;
    }
}

6.1.5 publish messages to RabbitMQ, create test classes and producers

@SpringBootTest
@RunWith(SpringRunner.class)
public class Mytest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publisherTest(){
        rabbitTemplate.convertAndSend("springboot-topic-exchange","xxxx.java.12dssad","Is that so?");
        System.out.println("send message");
    }
}

6.1.6 create consumer listening message

@Component
public class Consumer {
    @RabbitListener(queues = "springboot-queue")
    public void consumer1(String msg, Channel channel, Message message){
        System.out.println("Consumers get:"+msg);
        System.out.println("msg = "+message);
    }
}

The results are as follows:

 

6.2 manual Ack

6.2.1 add profile

spring.rabbitmq.listener.simple.acknowledge-mode=manual

6.2.2 manual ack

At this time, if you run Test multiple times, you will receive multiple messages that are not consumed

@Component
public class Consumer {
    @RabbitListener(queues = "springboot-queue")
    public void consumer1(String msg, Channel channel, Message message) throws IOException {
        System.out.println("Consumers get:"+msg);
        System.out.println("msg = "+message+" "+message.getMessageProperties().getDeliveryTag());
        int i = 1/0;
        // Manual ack
        // Send the sequence number of the message ack message.getMessageProperties().getDeliveryTag()
        // Whether multiple messages ack false together
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

This has been run for three times. On the third time, multiple non consumed objects will be directly generated

 

 

 

7, Other operations of RabbitMQ

7.1 message reliability

RabbitMQ transaction: the transaction can ensure 100% message delivery. The log can be recorded through transaction rollback, and the current message will be sent again regularly later. The efficiency of transaction operation is too low. After adding transaction operation, the operation efficiency is at least 100 times slower than usual.

In addition to transactions, RabbitMQ also provides a confirmation mechanism for Confirm, which is much more efficient than transactions.

 

7.2 SpringBoot implementation

7.2.1 preparation of configuration files

#Solve data security problems
spring.rabbitmq.publisher-confirm-type=simple
spring.rabbitmq.publisher-returns=true

7.2.2 enable Confirm and Return

@Component
public class ConfirmReturnCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    // Adding this bean to the container will call this method (init method, @ PostConstruct tag method)
    @PostConstruct//Equivalent to init of bean life cycle
    public void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean b, String s) {
        System.out.println("CorrelationData"+correlationData);
        System.out.println("s="+s);
        if (b){
            System.out.println("Message arrival switch");
        }
    }
    // return mechanism. Generally, there is no callback. It is called only when the message of the switch cannot be written to the queue
    @Override
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {
        System.out.println("message"+message);
    }
}

 

7.3 avoid repeated consumption of messages ()

Repeated consumption of messages will cause problems for non idempotent line operations

The reason for the repeated consumption message is that the consumer did not give RabbitMQ an ack

Repeated consumption

In order to solve the problem of repeated consumption of messages, Redis can be used. Before consumers consume messages, the message id is now put in Redis,

id-0 (executing business)

id-1 (successful execution of business)

If the ACK fails, when RabbitMQ sends the message to other consumers, it first executes setnx. If the key already exists, it obtains its value. If it is 0, the current consumer will do nothing. If it is 1, it will directly ack.

Extreme case: the first consumer has a deadlock when executing the business. On the basis of setnx, set a lifetime for the key.

Producer, specify messageId when sending a message

Consumers operate redis according to specific business logic when consuming messages

7.4 how does springboot realize message non repetition

7.4.1 import dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

7.4.2 preparation of configuration files

#Configure redis
spring.redis.host=8.130.166.101
spring.redis.port=6379

7.4.3 modify producer

@SpringBootTest
@RunWith(SpringRunner.class)
public class Mytest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publisherTest(){
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("springboot-topic-exchange","xxxx.java.12dssad","Is that so?"+System.currentTimeMillis(),correlationData);
        System.out.println("send message");
    }
}

7.4.4 modifying consumers

@Component
public class Consumer {
    @Autowired
    private RedisTemplate redisTemplate;
    @RabbitListener(queues = "springboot-queue")
    public void consumer1(String msg, Channel channel, Message message) throws IOException {

        System.out.println("Consumers get:"+msg);
        System.out.println("msg = "+message+" "+message.getMessageProperties().getDeliveryTag());
        // Get the unique id of the processed message
        String id = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");
        System.out.println("id="+id);
 // If the setting is successful, the message has not been processed
        // The id value is 0 for processing and 1 for processing completion
        if (redisTemplate.opsForValue().setIfAbsent(id,"0",10, TimeUnit.SECONDS)){
            System.out.println("Consumer, deal with the business:"+msg);
            //After processing, it becomes 1
            redisTemplate.opsForValue().set(id,"1",10,TimeUnit.SECONDS);

            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }else {
            // If the key cannot be set, it indicates that a consumer is already processing
            if (redisTemplate.opsForValue().get(id).equals("1")){

                // Manual ack
                // Send the sequence number of the message ack message.getMessageProperties().getDeliveryTag()
                // Whether multiple messages ack false together
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
            }
        }
    }
}

If the first run does not come out, the second run is allowed. If the second run does not come out, it is wrong

 

Posted by semtex on Wed, 24 Nov 2021 14:06:30 -0800