Message middleware RabbitMQ

Keywords: Java RabbitMQ message queue MQ

1, Message queue

1.1 related concepts of MQ

1.1.1 what is MQ

MQ(message queue), in its literal sense, is essentially a queue. FIFO is first in, first out, but the contents stored in the queue are messages. It is also a cross process communication mechanism for upstream and downstream message delivery. In the Internet architecture, MQ is a very common upstream and downstream "logical decoupling + physical decoupling" message communication service. After using MQ, the upstream of message sending only needs to rely on MQ without relying on other services.

1.1.2 why use MQ

  • Flow peak elimination
    For example, if the order system can process 10000 orders at most, this processing capacity is more than enough to deal with orders in the normal period. In the normal period, we can return the results one second after we place an order. However, in the peak period, if 20000 orders are placed, the operating system can not handle them. Users can only be restricted from placing orders after more than 10000 orders. Using the message queue as a buffer, we can cancel this restriction and disperse the orders placed in one second into a period of time. At this time, some users may not receive the successful operation of placing an order until more than ten seconds after placing an order, but it is better than the experience of not placing an order.
  • Application decoupling
    Taking e-commerce applications as an example, there are order system, inventory system, logistics system and payment system. After the user creates an order, if the inventory system, logistics system and payment system are coupled and invoked, any subsystem fails, which will cause abnormal ordering operation. When the method is changed to message queue based, the problem of inter system call will be reduced a lot. For example, the logistics system needs a few minutes to repair due to failure. In these minutes, the memory to be processed by the logistics system is cached in the message queue, and the user's order can be completed normally. After the logistics system is restored, you can continue to process the order information. Medium order users can't feel the failure of the logistics system and improve the availability of the system.
  • Asynchronous processing
    Some inter service calls are asynchronous. For example, a calls B, and B takes a long time to execute, but a needs to know when B can complete execution. In the past, there were generally two ways. A calls B's query api for a period of time. Or A provides a callback api. After B is executed, api is called to notify the A service. Neither of these two methods is very elegant. Using the message bus can easily solve this problem. After a calls the B service, a only needs to listen to the message processed by B. when B processing is completed, it will send a message to MQ, which will forward the message to the a service. In this way, service a does not need to call B's query api or provide callback api. Similarly, service B does not need to do these operations. Service a can also get the message of successful asynchronous processing in time.

1.1.3 classification of MQ

1.ActiveMQ

  • advantage:
    Single machine throughput, timeliness, ms level, high availability, high availability based on master-slave architecture, low message reliability and low probability of data loss
  • Disadvantages:
    The official community now maintains ActiveMQ 5.x less and less, and uses less in high-throughput scenarios.

2.Kafka
Kafka is the killer mace of big data. When it comes to message transmission in the field of big data, Kafka cannot be bypassed. This message middleware born for big data is famous for its throughput of millions of TPS. It has quickly become a favorite in the field of big data and plays an important role in the process of data collection, transmission and storage. It has been adopted by LinkedIn, Uber, Twitter, Netflix and other large companies.

  • advantage:
    Excellent performance, single machine write TPS is about one million pieces / s, and the biggest advantage is high throughput. Timeliness: ms level availability is very high. kafka is distributed. One data has multiple copies, and a few machines go down, which will not lose data and cause unavailability. Consumers use Pull to obtain messages. Messages are orderly. Through control, all messages can be consumed only once; Excellent third-party Kafka Web management interface kafka manager; It is mature in the log field and is used by many companies and open source projects; Function support: the function is relatively simple. It mainly supports simple MQ functions. Real time computing and log collection in the field of big data are used on a large scale
  • Disadvantages:
    If the Kafka single machine has more than 64 queues / partitions, the load will soar obviously. The more queues, the higher the load, and the longer the response time of sending messages. Using short polling mode, the real-time performance depends on the polling interval. Consumption failure does not support retry; Support message order, but when an agent goes down, it will cause message disorder and slow community update;

3.RocketMQ
RocketMQ is an open-source product from Alibaba and is implemented in Java language. Kafka is referred to in the design and some improvements have been made. It is widely used by Alibaba in order, transaction, recharge, stream computing, message push, log stream processing, binglog distribution and other scenarios.

  • advantage:
    The single machine throughput is 100000 levels, and the availability is very high. In the distributed architecture, messages can be lost. The MQ function is relatively perfect, but it is still distributed and has good scalability. It supports the accumulation of 1 billion levels of messages and will not lead to performance degradation. The source code is java. We can read the source code ourselves and customize our own MQ
  • Disadvantages:
    There are not many supported client languages. At present, java and c + +, of which c + + is immature; The community is generally active and does not implement JMS and other interfaces in the MQ core. Some systems need to modify a lot of code to migrate

4.RabbitMQ
Released in 2007, it is a reusable enterprise message system based on AMQP (Advanced message queuing protocol). It is one of the most mainstream message middleware at present.

  • advantage:
    Due to the high concurrency of erlang language, its performance is good; With a throughput of 10000, MQ features are relatively complete, robust, stable, easy to use, cross platform, support multiple languages, such as Python, Ruby,. NET, Java, JMS, C, PHP, ActionScript, XMPP, STOMP, etc., and support AJAX documents; The management interface provided by open source is very good, easy to use, and the community is highly active; The update frequency is quite high https://www.rabbitmq.com/news.html
  • Disadvantages:
    The commercial version needs to be charged, and the learning cost is high

1.1.4 selection of MQ

  1. Kafka
    kafka is mainly characterized by processing message consumption based on Pull mode and pursuing high throughput. Its initial purpose is to collect and transmit logs, which is suitable for the data collection business of Internet services that generate a large amount of data. It is suggested that large companies can choose it. If there is log collection function, it must be the first choice kafka. kafka video connection of shangsilicon Valley official website http://www.gulixueyuan.com/course/330/tasks
  2. RocketMQ
    Born in the field of financial Internet and demanding high reliability, especially in the case of order deduction and business peak cutting in e-commerce, when a large number of transactions pour in, the back end may not be able to handle them in time. RocketMQ may be more reliable in stability. These business scenarios have been tested many times in Alibaba double 11. If your business has the above concurrent scenarios, it is recommended to choose RocketMQ.
  3. RabbitMQ
    Combined with the concurrency advantages of erlang language itself, it has good performance, timeliness, microsecond level, high community activity, and convenient management interface. If your data volume is not so large, small and medium-sized companies give priority to RabbitMQ with relatively complete functions.

1.2,RabbitMQ

1.2.1 concept of RabbitMQ

RabbitMQ is a message oriented middleware: it accepts and forwards messages. You can regard it as an express site. When you want to send a package, you put your package in the express station, and the courier will eventually send your express to the recipient. According to this logic, RabbitMQ is an express station, and a courier will deliver the express for you. The main difference between RabbitMQ and express station is that it does not process express mail, but receives, stores and forwards message data.

1.2.2 four core concepts

  • producer
    The program that generates the data sending message is the producer
  • Switch
    Switch is a very important part of RabbitMQ. On the one hand, it receives messages from producers, on the other hand, it pushes messages to queues. The switch must know exactly how to handle the messages it receives, whether to push these messages to a specific queue or multiple queues, or discard the messages, which depends on the switch type
  • queue
    Queue is a data structure used internally by RabbitMQ. Although messages flow through RabbitMQ and applications, they can only be stored in the queue. The queue is only constrained by the memory and disk limitations of the host. It is essentially a large message buffer. Many producers can send messages to a queue, and many consumers can try to receive data from a queue. This is how we use queues
  • consumer
    Consumption and reception have similar meanings. Most of the time, consumers are a program waiting to receive messages. Note that producers, consumers, and message middleware are often not on the same machine. The same application can be both a producer and a consumer.

1.2.3 RabbitMQ core



1.2.4 introduction to terms

  • Broker:
    RabbitMQ Server is the application that receives and distributes messages. RabbitMQ Server is the Message Broker
  • Virtual host:
    Designed for multi tenancy and security factors, the basic components of AMQP are divided into a virtual group, which is similar to the concept of namespace in the network. When multiple different users use the services provided by the same RabbitMQ server, they can be divided into multiple vhosts. Each user creates an exchange / queue in its own vhost
  • Connection:
    TCP connection between publisher / consumer and broker
  • Channel:
    If a Connection is established every time RabbitMQ is accessed, the overhead of establishing a TCP Connection when the message volume is large will be huge and the efficiency will be low. A channel is a logical Connection established within a Connection. If the application supports multithreading, each thread usually creates a separate channel for communication. The AMQP method contains a channel id to help the client and message broker identify the channel, so the channels are completely separated. As a lightweight Connection, channel greatly reduces the overhead of establishing TCP Connection by the operating system
  • Exchange:
    The message arrives at the first stop of the broker, matches the routing key in the query table according to the distribution rules, and distributes the message to the queue. Common types are: direct (point-to-point), topic (publish subscribe) and fan out (multicast)
  • Queue:
    The message was finally sent here to wait for the consumer to pick it up
  • Binding:
    For the virtual connection between exchange and queue, the binding can contain the routing key, and the binding information is saved in the query table in exchange for the distribution basis of message s

2, RabbitMQ getting started

2.1. Create producer and consumer modules

Create producer rabbitmq producer and consumer rabbitmq consumer modules

2.2. Add maven dependency

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.14.0</version>
</dependency>

2.3. Write connection tools

public class ConnectionUtil {
    public static Connection getConnection() throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setVirtualHost("/");
        Connection connection = factory.newConnection();
        return connection;
    }
}

2.4 compilation

public class RabbitmqProducer {
    private final static String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        //Message content to send
        String message = "Hello World";
        /**
         * basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         * String exchange: Switch name, if not specified, the default queue name is used
         * String routingKey: Routing key. The simple mode can pass the queue name
         * BasicProperties props: Other properties of the message
         * byte[] body: Message content
         */
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println("Send complete");

        //close resource
        channel.close();
        connection.close();
    }
}

2.5 compilation of consumer

public class RabbitmqConsumer {
    private final static String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

2.6 summary


In the model above, there are the following concepts:

  • P: The producer is the program that sends the message
  • C: Consumer: the receiver of the message will always wait for the message to arrive.
  • Queue: message queue, the red part in the figure represents the message buffer reserved by consumers; The producer delivers the message to it, and the consumer takes the message out of it. The producer sends the message to the queue. The consumer receives messages from this queue.

3, AMQP

3.1 concept introduction

AMQP is an application layer standard advanced message queuing protocol that provides unified messaging services. It is an open standard of application layer protocol and is designed for message oriented middleware.

AMQP is a binary protocol with some modern characteristics: multi-channel, negotiation, asynchronous, secure, platform expansion, neutral and efficient.

RabbitMQ is an Erlang implementation of AMQP protocol.

conceptexplain
ConnectionA network connection, such as a TCP/IP socket connection.
Session sessionNamed conversations between endpoints. In a session context, ensure "exactly once".
Channel channelAn independent bidirectional data flow channel in a multiplexed connection. Provides a physical transport medium for the session.
Client clientThe initiator of an AMQP connection or session. AMQP is asymmetric, the client produces and consumes messages, and the server stores and routes these messages.
Service node BrokerService node of message oriented middleware; In general, a RabbitMQ Broker can be regarded as a RabbitMQ server.
EndpointEither party to the AMQP conversation. An AMQP connection consists of two endpoints (a client and a server).
ConsumerA client program that requests messages from a message queue.
ProducerA client application that publishes messages to the switch.

3.2 RabbitMQ operation process

In the RabbitMQ getting started case:

Producer sends message

  1. The producer creates a Connection, opens a Channel, and connects to the RabbitMQ Broker;
  2. Declare the queue and set properties; For example, whether to exclude, persist, and delete automatically;
  3. Bind the routing key (empty string) to the queue;
  4. Send a message to RabbitMQ Broker;
  5. Close the channel;
  6. Close the connection;

Consumer receives message

  1. The consumer creates a Connection, opens a Channel, and connects to the RabbitMQ Broker
  2. Request the Broker to consume the messages in the corresponding queue and set the corresponding callback function;
  3. Wait for the Broker to respond, close the message in the delivery response queue, and the consumer receives the message;
  4. Acknowledge (ack, automatic acknowledgement) the received message;
  5. RabbitMQ deletes the corresponding confirmed message from the queue;
  6. Close the channel;
  7. Close the connection;

3.3 description of producer circulation process

  1. The client establishes a connection with the proxy server Broker. The newConnection() method will be called, which will further encapsulate the message of Protocol Header 0-9-1 and send it to the Broker, so as to inform the Broker that AMQPO-9-1 protocol is adopted for this interaction, and then the Broker returns Connection.Start to establish the connection. Connection. Start /. Start OK and connection. Tune /. Tune OK are involved in the connection process, Connection. Open /. Open OK the interaction of these six commands.
  2. The client calls the connection.createChannel method. This method opens the channel, sends the wrapped channel.open command to the Broker, and waits for the channel.basicPublish method. The corresponding AMQP command is Basic.Publish, which includes content Header and content Body(). The content Header contains the properties of the message body, such as delivery mode, priority, etc. the content Body contains the message body itself.
  3. When the client needs to close the resource after sending the message, it involves the command interaction of Channel.Close and Channel.Close-ok with connection.close and Connection.Close-Ok.

3.4 description of consumer circulation process

  1. The consumer client establishes a connection with the proxy server Broker. The newConnection() method will be called, which will further encapsulate the message of Protocol Header 0-9-1 and send it to the Broker, so as to inform the Broker that AMQPO-9-1 protocol is adopted for this interaction, and then the Broker returns Connection.Start to establish the connection. Connection. Start /. Start OK and connection. Tune /. Tune OK are involved in the connection process, Connection. Open /. Open OK the interaction of these six commands.
  2. The consumer client calls the connection.createChannel method. Like the producer client, the protocol involves the channel. Open / open OK command.
  3. Before real consumption, the consumer client needs to send a basic. Consumption command to the Broker (that is, call the channel.basicConsume method) to set the Channel to the receiving mode, and then the Broker returns a basic. Consumption - OK receipt to tell the consumer client that it is ready for consumption.
  4. The Broker pushes a message to the consumer client, that is, the Basic.Deliver command. Like the Basic.Publish command, this command will carry the Content Header and Content Body.
  5. After receiving the message and consuming it correctly, the consumer sends a confirmation to the Broker, that is, the Basic.Ack command.
  6. When the client needs to close the resource after sending the message, it involves the command interaction of Channel.Close and Channel.Close-ok with connection.close and Connection.Close-Ok.

4, RabbitMQ five message modes

Message classification

  1. Work queue mode
    • Hello World (simple mode)
    • Work queues
  2. Publish / subscribe mode
    • Publish/Subscribe (broadcast mode)
    • Routing (routing mode)
    • Topics (wildcard mode)

4.1. hello world simple mode


The code is consistent with the entry program

4.2 Work queues mode

The default traditional queue is shared consumption, which is unfair; if each consumer's speed is different, shared consumption is unfair, and those who can do more should do more.
Compared with the simple mode of the entry program, Work Queues has one or more consumers, and multiple consumers consume messages in the same queue together.

Application scenario: when tasks are too heavy or there are many tasks, using work queue can improve the speed of task processing.

The code of Work Queues is almost the same as that of the simple mode of the entry program; you can completely copy and copy one more consumer to test the simultaneous consumption of messages by multiple consumers.

1. Producer

public class WorkQueueProducer {
    private final static String QUEUE_NAME = "work_queues";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        /**
         * basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         * String exchange: Switch name, if not specified, the default queue name is used
         * String routingKey: Routing key. The simple mode can pass the queue name
         * BasicProperties props: Other properties of the message
         * byte[] body: Message content
         */
        for (int i = 1; i <= 10; i++) {
            //Message content to send
            String message = "Producer production message----" + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        }

        System.out.println("Send complete");

        //close resource
        channel.close();
        connection.close();
    }
}

2. Consumer 1

public class WorkQueueConsumer1 {
    private final static String QUEUE_NAME = "work_queues";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //Create a consumer; and set up message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

3. Consumer 2

public class WorkQueueConsumer2 {
    private final static String QUEUE_NAME = "work_queues";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //Create a consumer; and set up message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

4.3 publish / subscribe


In the publish / subscribe model, there is an exchange role, and the process changes slightly

  • Publisher: the producer, that is, the program that sends the message, but it is no longer sent to the queue, but to X (switch)
  • Exchange: switch, on the one hand, receives messages sent by producers. On the other hand, you know how to process messages, such as delivering to a special queue, delivering to all queues, or discarding messages. How to operate depends on the type of exchange. There are three types of exchange:
    • Fanout: broadcast and deliver messages to all queues bound to the switch
    • Direct: direct, which sends messages to queues that match the specified routing key
    • Topic: wildcard, which gives the message to the queue conforming to the routing pattern
  • Consumer: the consumer, as before, subscribes to the queue without change
  • Queue: as before, the message queue receives messages and caches messages.

RabbitMQ switch type

  • Direct exchange
  • Fanout exchange
  • Topic exchange
  • Headers exchange

Exchange (switch) is only responsible for forwarding messages and does not have the ability to store messages. Therefore, if there is no queue bound to exchange or no queue that meets the routing rules, the messages will be lost!

4.4. Broadcast mode (Fanout)

In broadcast mode, the message sending process is as follows:

  1. There can be multiple queues
  2. Each queue is bound to an Exchange (switch)
  3. The message sent by the producer can only be sent to the switch. The switch determines which queue to send, but the producer cannot decide
  4. The switch sends messages to all queues that have been bound
  5. Consumers who subscribe to the queue can get the message


1. Producer

public class FanoutProducer {
    //Switch name
    static final String FANOUT_EXCHAGE = "fanout_exchange";
    // Queue name
    static final String FANOUT_QUEUE1 = "fanout_queue1";
    // Queue name
    static final String FANOUT_QUEUE2 = "fanout_queue2";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(FANOUT_QUEUE1, true, false, false, null);
        channel.queueDeclare(FANOUT_QUEUE2, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(FANOUT_QUEUE1, FANOUT_EXCHAGE, "");
        channel.queueBind(FANOUT_QUEUE2, FANOUT_EXCHAGE, "");

        /**
         * basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         * String exchange: Switch name, if not specified, the default queue name is used
         * String routingKey: Routing key. The simple mode can pass the queue name
         * BasicProperties props: Other properties of the message
         * byte[] body: Message content
         */
        for (int i = 1; i <= 10; i++) {
            //Message content to send
            String message = "Producer production message----Publish subscribe mode----" + i;
            channel.basicPublish(FANOUT_EXCHAGE, "", null, message.getBytes());
        }

        System.out.println("Send complete");

        //close resource
        channel.close();
        connection.close();
    }
}

2. Consumer 1

public class FanoutConsumer1 {
    //Switch name
    static final String FANOUT_EXCHAGE = "fanout_exchange";
    // Queue name
    static final String FANOUT_QUEUE1 = "fanout_queue1";
    // Queue name
    static final String FANOUT_QUEUE2 = "fanout_queue2";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(FANOUT_QUEUE1, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(FANOUT_QUEUE1, FANOUT_EXCHAGE, "");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(FANOUT_QUEUE1, true, consumer);
    }
}

3. Consumer 2

public class FanoutConsumer2 {
    //Switch name
    static final String FANOUT_EXCHAGE = "fanout_exchange";
    // Queue name
    static final String FANOUT_QUEUE1 = "fanout_queue1";
    // Queue name
    static final String FANOUT_QUEUE2 = "fanout_queue2";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT);


        /**
         *  Declaration queue
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(FANOUT_QUEUE2, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(FANOUT_QUEUE1, FANOUT_EXCHAGE, "");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(FANOUT_QUEUE2, true, consumer);
    }
}

4.5 routing mode (Direct)

In Fanout mode, a message will be consumed by all subscribed queues. However, in some scenarios, we want different messages to be consumed by different queues. In this case, Exchange of Direct type will be used.

Under the Direct model

  • The binding between the queue and the switch cannot be arbitrary, but a routing key should be specified
  • When sending a message to Exchange, the sender of the message must also specify the RoutingKey of the message.
  • Exchange will no longer deliver messages to each bound queue, but will judge according to the Routing key of the message. Messages will be received only if the Routing key of the queue is completely consistent with the Routing key of the message

The Routing mode requires the queue to specify the routing key when binding the switch, and the message will be forwarded to the queue matching the routing key.

producer

public class DirectProducer {
    //Switch name
    static final String DIRECT_EXCHAGE = "direct_exchange";
    // Queue name
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    // Queue name
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT, true, false, false, null);
        channel.queueDeclare(DIRECT_QUEUE_UPDATE, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(DIRECT_QUEUE_INSERT, DIRECT_EXCHAGE, "insert");
        channel.queueBind(DIRECT_QUEUE_UPDATE, DIRECT_EXCHAGE, "update");

        //Message content to send
        String message = "New products have been added. Routing mode; routing key by insert ";

        /**
         * basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         * String exchange: Switch name, if not specified, the default queue name is used
         * String routingKey: Routing key. The simple mode can pass the queue name
         * BasicProperties props: Other properties of the message
         * byte[] body: Message content
         */
        channel.basicPublish(DIRECT_EXCHAGE, "insert", null, message.getBytes());

        // Send message
        message = "Modified item. Routing mode; routing key by update" ;

        /**
         * basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         * String exchange: Switch name, if not specified, the default queue name is used
         * String routingKey: Routing key. The simple mode can pass the queue name
         * BasicProperties props: Other properties of the message
         * byte[] body: Message content
         */
        channel.basicPublish(DIRECT_EXCHAGE, "update", null, message.getBytes());

        System.out.println("Send complete");

        //close resource
        channel.close();
        connection.close();
    }
}

Consumer 1

public class DirectConsumer1 {
    //Switch name
    static final String DIRECT_EXCHAGE = "direct_exchange";
    // Queue name
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    // Queue name
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(DIRECT_QUEUE_INSERT, DIRECT_EXCHAGE, "insert");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(DIRECT_QUEUE_INSERT, true, consumer);
    }
}

Consumer 2

public class DirectConsumer2 {
    //Switch name
    static final String DIRECT_EXCHAGE = "direct_exchange";
    // Queue name
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    // Queue name
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(DIRECT_QUEUE_UPDATE, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(DIRECT_QUEUE_UPDATE, DIRECT_EXCHAGE, "insert");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received

                System.out.println("The received message is:" + new String(body,"UTF-8"));
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(DIRECT_QUEUE_UPDATE, true, consumer);
    }
}

4.6. Wildcard pattern (Topic)

Compared with Direct, Topic Exchange can route messages to different queues according to the RoutingKey. However, Topic Exchange allows the queue to use wildcards when Binding routing keys!

Routingkey is generally composed of one or more words, and multiple words are separated by ".", such as user.insert
Wildcard rule:

#: matches one or more words
*: no more, no less, exactly 1 word

explain

  • Queue1: the binding is China. #, so all routing key s starting with China. Will be matched. Including china.news and china.weather
  • Queue2: the binding is #. News, so all routing key s ending in. News will be matched. Including china.news and japan.news

producer

public class TopictProducer {
    //Switch name
    static final String TOPIC_EXCHAGE = "topic_exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);


        // Send message
        String message = "China news. Topic pattern; routing key by china.news";
        channel.basicPublish(TOPIC_EXCHAGE, "china.news", null, message.getBytes());
        System.out.println("Sent message:" + message);

        // Send message
        message = "The weather in China. Topic pattern; routing key by china.weather";
        channel.basicPublish(TOPIC_EXCHAGE, "china.weather", null, message.getBytes());
        System.out.println("Sent message:" + message);

        // Send message
        message = "Japanese news. Topic pattern; routing key by japan.news";
        channel.basicPublish(TOPIC_EXCHAGE, "japan.news", null, message.getBytes());
        System.out.println("Sent message:" + message);

        // Send message
        message = "The weather in Japan. Topic pattern; routing key by japan.weather";
        channel.basicPublish(TOPIC_EXCHAGE, "japan.weather", null, message.getBytes());
        System.out.println("Sent message:" + message);

        //close resource
        channel.close();
        connection.close();
    }
}

Consumer 1

public class TopicConsumer1 {
    //Switch name
    static final String TOPIC_EXCHAGE = "topic_exchange";
    // Queue name
    static final String TOPIC_QUEUE1 = "topic_queuq1";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(TOPIC_QUEUE1, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(TOPIC_QUEUE1, TOPIC_EXCHAGE, "china.#");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received
                System.out.println("The received message is:" + new String(body, "UTF-8"));
                System.out.println("-------------------");
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(TOPIC_QUEUE1, true, consumer);
    }
}

Consumer 2

public class TopicConsumer2 {
    //Switch name
    static final String TOPIC_EXCHAGE = "topic_exchange";
    // Queue name
    static final String TOPIC_QUEUE2 = "topic_queuq2";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(TOPIC_QUEUE2, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(TOPIC_QUEUE2, TOPIC_EXCHAGE, "japan.#");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received
                System.out.println("The received message is:" + new String(body,"UTF-8"));
                System.out.println("-------------------");
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(TOPIC_QUEUE2, true, consumer);
    }
}

Consumer 3

public class TopicConsumer3 {
    //Switch name
    static final String TOPIC_EXCHAGE = "topic_exchange";
    // Queue name
    static final String TOPIC_QUEUE3 = "topic_queuq3";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(TOPIC_QUEUE3, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(TOPIC_QUEUE3, TOPIC_EXCHAGE, "#.news");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received
                System.out.println("The received message is:" + new String(body,"UTF-8"));
                System.out.println("-------------------");
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(TOPIC_QUEUE3, true, consumer);
    }
}

Consumer 4

public class TopicConsumer4 {
    //Switch name
    static final String TOPIC_EXCHAGE = "topic_exchange";
    // Queue name
    static final String TOPIC_QUEUE4 = "topic_queuq4";

    public static void main(String[] args) throws IOException, TimeoutException {
        // Create connection
        Connection connection = ConnectionUtil.getConnection();

        // Create channel
        Channel channel = connection.createChannel();

        /**
         * Claim switch
         * exchangeDeclare(String exchange, String type)
         * String exchange: Switch name
         * String type: Switch type, fanout, topic, direct, headers
         */
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);


        /**
         * queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
         * String queue: Queue name
         * boolean durable: Define persistence queue
         * boolean exclusive: Exclusive connection
         * boolean autoDelete: Delete queue automatically when not in use
         * Map<String, Object> arguments: Queue other parameters
         */
        channel.queueDeclare(TOPIC_QUEUE4, true, false, false, null);

        /**
         * Queue bound switch
         * queueBind(String queue, String exchange, String routingKey)
         * String queue: Queue name
         * String exchange: Switch name
         * String routingKey: Routing key
         */
        channel.queueBind(TOPIC_QUEUE4, TOPIC_EXCHAGE, "#.weather");

        //Creating consumers; And set message processing
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            /**
             * handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
             * String consumerTag: The message sender tag can be specified in channel.basicConsume
             * Envelope envelope: The content of the message package, from which you can obtain the message id, message routingkey, switch, message and retransmission flag (whether you need to resend after receiving the message failure)
             * AMQP.BasicProperties properties: Attribute information
             * byte[] body: news
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //Routing key
                System.out.println("route key Is:" + envelope.getRoutingKey());
                //Switch
                System.out.println("The switch is:" + envelope.getExchange());
                //Message id
                System.out.println("news id Is:" + envelope.getDeliveryTag());
                //Messages received
                System.out.println("The received message is:" + new String(body,"UTF-8"));
                System.out.println("-------------------");
            }
        };

        /**
         * Listen for messages
         * basicConsume(String queue, boolean autoAck, Consumer callback)
         * String queue: Queue name
         * boolean autoAck: Auto confirm:
         *                      true: Indicates that the message is automatically replied to mq after it is received, and mq will delete the message after receiving the reply,
         *                      false: Indicates that manual confirmation is required
         * Consumer callback: Callback after receiving message
         */
        //Listen for messages
        channel.basicConsume(TOPIC_QUEUE4, true, consumer);
    }
}

5, Spring integrates RabbitMQ

5.1. Construction project

5.1.1 create project

Create producer project spring rabbitmq producer

5.1.2. Add dependency

Add dependency

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>2.4.0</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

5.1.3 configuration integration

Create rabbitmq.properties connection parameter configuration file

rabbitmq.host=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtual-host=/

Create the spring-rabbitmq.xml integration configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--Load profile-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- definition rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    <!--Define management switches, queues-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--Define a persistent queue. If it does not exist, it will be created automatically; Bind to default switch if not bound to switch
    The default switch type is direct,The name is:"",The routing key is the name of the queue
    -->
    <!--
        id: bean Name of
        name: queue Name of
        auto-declare:Automatic creation
        auto-delete:Automatically delete. When the last consumer disconnects from the queue, the queue is automatically deleted
        exclusive:Exclusive
        durable: Persistent
    -->
    <rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/>

    <!--Work queue mode-->
    <rabbit:queue id="spring_word_queue" name="spring_word_queue" auto-declare="true"/>


    <!-- radio broadcast; All queues can receive messages -->
    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_fanout_queue1" name="spring_fanout_queue1" auto-declare="true"/>
    <rabbit:queue id="spring_fanout_queue2" name="spring_fanout_queue2" auto-declare="true"/>

    <!--Define broadcast type switch; And bind the above two queues-->
    <rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="spring_fanout_queue1"></rabbit:binding>
            <rabbit:binding queue="spring_fanout_queue2"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <!-- route; Match queue received message-->
    <rabbit:queue id="spring_direct_queue1" name="spring_direct_queue1" auto-declare="true"/>
    <rabbit:queue id="spring_direct_queue2" name="spring_direct_queue2" auto-declare="true"/>

    <!--Define the persistent queue in the routing switch. If it does not exist, it will be created automatically-->
    <rabbit:direct-exchange name="spring_direct_exchange">
        <rabbit:bindings>
            <!--direct Switch binding queue of type  key : route key  queue: Queue name-->
            <rabbit:binding queue="spring_direct_queue1" key="red"></rabbit:binding>
            <rabbit:binding queue="spring_direct_queue2" key="blue"></rabbit:binding>
            <rabbit:binding queue="spring_direct_queue2" key="yellow"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- Wildcards;*Match a word,#Match multiple words -- >
    <!--Define the persistent queue in the broadcast switch. If it does not exist, it will be created automatically-->
    <rabbit:queue id="spring_topic_queue1" name="spring_topic_queue1" auto-declare="true"/>
    <rabbit:queue id="spring_topic_queue2" name="spring_topic_queue2" auto-declare="true"/>
    <rabbit:queue id="spring_topic_queue3" name="spring_topic_queue3" auto-declare="true"/>
    <rabbit:queue id="spring_topic_queue4" name="spring_topic_queue4" auto-declare="true"/>

    <rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange">
        <rabbit:bindings>
            <rabbit:binding pattern="china.*" queue="spring_topic_queue1"></rabbit:binding>
            <rabbit:binding pattern="japan.#" queue="spring_topic_queue2"></rabbit:binding>
            <rabbit:binding pattern="#.news" queue="spring_topic_queue3"></rabbit:binding>
            <rabbit:binding pattern="#.weather" queue="spring_topic_queue4"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!--definition rabbitTemplate Object operation can easily send messages in code-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>

5.1.4 sending messages

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class SpringProducerTest {

    //1. Inject rabbit template
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * Send queue messages only
     * The default switch type is direct
     * The name of the switch is empty, and the routing key is the name of the queue
     */
    @Test
    public void queueTest() {
        //The routing key has the same name as the queue
        rabbitTemplate.convertAndSend("spring_queue", "hello spring rabbitmq");
    }

    @Test
    public void workQueueTest() {
        //The routing key has the same name as the queue
        rabbitTemplate.convertAndSend("spring_word_queue", "hello spring rabbitmq");
        rabbitTemplate.convertAndSend("spring_word_queue", "hello spring rabbitmq");
    }

    /**
     * Send broadcast
     * The switch type is fanout
     * All queues bound to the switch can receive messages
     */
    @Test
    public void fanoutTest() {
        /**
         * convertAndSend(String exchange, String routingKey, Object object)
         * exchange: Switch name
         * routingKey: Routing key name (broadcast setting is empty)
         * object: Message content sent
         */
        rabbitTemplate.convertAndSend("spring_fanout_exchange", "", "Send to spring_fanout_exchange Switch broadcast message 1");
        rabbitTemplate.convertAndSend("spring_fanout_exchange", "", "Send to spring_fanout_exchange Switch broadcast message 2");
    }

    /**
     * Send route
     * The switch type is direct
     * All queues bound to the routing key can receive messages
     */
    @Test
    public void directTest() {
        /**
         * convertAndSend(String exchange, String routingKey, Object object)
         * exchange: Switch name
         * routingKey: Routing key name
         * object: Message content sent
         */
        rabbitTemplate.convertAndSend("spring_direct_exchange", "red", "Send to spring_direct_exchange Switch red Routing message");
        rabbitTemplate.convertAndSend("spring_direct_exchange", "blue", "Send to spring_direct_exchange Switch blue Routing message");
        rabbitTemplate.convertAndSend("spring_direct_exchange", "yellow", "Send to spring_direct_exchange Switch yellow Routing message");
    }

    /**
     * wildcard
     * The switch type is topic
     * Wildcard matching routing keys, * for one word, # for multiple words
     * The matching queue bound to the switch can receive the corresponding message
     */
    @Test
    public void topicTest(){
        /**
         * convertAndSend(String exchange, String routingKey, Object object)
         * exchange: Switch name
         * routingKey: Routing key name
         * object: Message content sent
         */
        rabbitTemplate.convertAndSend("spring_topic_exchange", "china.news", "Send to spring_topic_exchange Switch china.new News of");
        rabbitTemplate.convertAndSend("spring_topic_exchange", "china.weather", "Send to spring_topic_exchange Switch china.weather News of");
        rabbitTemplate.convertAndSend("spring_topic_exchange", "japan.news", "Send to spring_topic_exchange Switch japan.news News of");
        rabbitTemplate.convertAndSend("spring_topic_exchange", "japan.weather", "Send to spring_topic_exchange Switch japan.weather News of");
    }
}

5.2. Building consumer Engineering

5.2.1 create project

Create consumer engineering spring rabbitmq consumer

5.2.2. Add dependency

Add dependency

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>2.4.0</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

5.2.3 configuration integration

Create rabbitmq.properties connection parameter configuration file

rabbitmq.host=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtual-host=/

Create the spring-rabbitmq.xml integration configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--Load profile-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- definition rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory"
                               host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    <!--Define management switches, queues-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <bean id="springQueueListener" class="com.java521.listener.SpringQueueListener"></bean>

    <bean id="springWorkQueueListener1" class="com.java521.listener.SpringWorkQueueListener1"></bean>
    <bean id="springWorkQueueListener2" class="com.java521.listener.SpringWorkQueueListener2"></bean>

    <bean id="fanoutListener1" class="com.java521.listener.FanoutListener1"></bean>
    <bean id="fanoutListener2" class="com.java521.listener.FanoutListener2"></bean>

    <bean id="directListener1" class="com.java521.listener.DirectListener1"></bean>
    <bean id="directListener2" class="com.java521.listener.DirectListener2"></bean>

    <bean id="topicListener1" class="com.java521.listener.TopicListener1"></bean>
    <bean id="topicListener2" class="com.java521.listener.TopicListener2"></bean>
    <bean id="topicListener3" class="com.java521.listener.TopicListener3"></bean>
    <bean id="topicListener4" class="com.java521.listener.TopicListener4"></bean>

    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
        <rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
        <rabbit:listener ref="springWorkQueueListener1" queue-names="spring_word_queue"/>
        <rabbit:listener ref="springWorkQueueListener2" queue-names="spring_word_queue"/>
        <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue1"/>
        <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue2"/>
        <rabbit:listener ref="directListener1" queue-names="spring_direct_queue1"/>
        <rabbit:listener ref="directListener2" queue-names="spring_direct_queue2"/>
        <rabbit:listener ref="topicListener1" queue-names="spring_topic_queue1"/>
        <rabbit:listener ref="topicListener2" queue-names="spring_topic_queue2"/>
        <rabbit:listener ref="topicListener3" queue-names="spring_topic_queue3"/>
        <rabbit:listener ref="topicListener4" queue-names="spring_topic_queue4"/>
    </rabbit:listener-container>
</beans

5.2.4 message listener

1. Simple queue listener

public class SpringQueueListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

2. Work queue listener 1

public class SpringWorkQueueListener1 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("spring_queue1: Receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

3. Work queue listener 2

public class SpringWorkQueueListener2 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("spring_queue2: Receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

4. Broadcast listener 1

public class FanoutListener1 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Broadcast listener 1: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

5. Broadcast listener 2

public class FanoutListener2 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Broadcast listener 2: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

6. Route listener 1

public class DirectListener1 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Route listener 1: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

7. Route listener 2

public class DirectListener2 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Route listener 2: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

8. Match listener 1

public class TopicListener1 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("General configuration listener 1: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

9. Match listener 2

public class TopicListener2 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Generic listener 2: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

10. Match listener 3

public class TopicListener3 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Generic listener 3: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

11. Match listener 4

public class TopicListener4 implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.printf("Generic listener 4: receive route name:%s,The routing key is:%s,The queue name is:%s Message:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

6, SpringBoot integrates RabbitMQ

6.1 introduction

In the Spring project, you can use Spring rabbit to operate RabbitMQ: https://github.com/spring-projects/spring-amqp

In particular, in the spring boot project, you only need to introduce the corresponding amqp initiator dependency. It is convenient to send messages using the RabbitTemplate and receive messages using annotations.

Generally, during the development process:

  • Producer Engineering:
    1. The application.yml file configures RabbitMQ related information;
    2. Write configuration classes in the producer project to create switches and queues and bind them
    3. Inject the RabbitTemplate object and send messages to the switch through the RabbitTemplate object
  • Consumer Engineering:
    1. The application.yml file configures RabbitMQ related information
    2. Create a message processing class to receive and process messages in the queue

6.2. Construction project

6.2.1 create project

Create springboot project producer project springboot rabbitmq producer

6.2.2. Add dependency

<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.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

6.2.3 configuration file

Create application.yml as follows

server:
  port: 9090
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /

2) Binding switches and queues
Create the configuration class RabbitMQConfig that the RabbitMQ queue is bound to the switch

@Configuration
public class RabbitMQConfig {
    //Simple queue
    public static final String SPRING_BOOT_SIMPLE_QUEUE = "spring_boot_simple_queue";
    //Work queue
    public static final String SPRING_BOOT_WORK_QUEUE = "spring_boot_work_queue";

    //Broadcast mode
    public static final String SPRING_BOOT_FANOUT_QUEUE1 = "spring_boot_fanout_queue1";
    public static final String SPRING_BOOT_FANOUT_QUEUE2 = "spring_boot_fanout_queue2";
    public static final String SPRING_BOOT_FANOUT_EXCHANGE = "spring_boot_fanout_exchange";

    //Routing mode
    public static final String SPRING_BOOT_DIRECT_QUEUE1 = "spring_boot_direct_queue1";
    public static final String SPRING_BOOT_DIRECT_QUEUE2 = "spring_boot_direct_queue2";
    public static final String SPRING_BOOT_DIRECT_EXCHANGE = "spring_boot_direct_exchange";

    //wildcard pattern 
    public static final String SPRING_BOOT_TOPIC_QUEUE1 = "spring_boot_topic_queue1";
    public static final String SPRING_BOOT_TOPIC_QUEUE2 = "spring_boot_topic_queue2";
    public static final String SPRING_BOOT_TOPIC_EXCHANGE = "spring_boot_topic_exchange";

    //Simple queue
    @Bean
    public Queue simpleQueue() {
        return new Queue(SPRING_BOOT_SIMPLE_QUEUE);
    }

    //Work queue
    @Bean
    public Queue workQueue() {
        return new Queue(SPRING_BOOT_WORK_QUEUE);
    }


    /**
     * Broadcast mode
     * Declare Fanout type switch
     */
    @Bean
    public FanoutExchange springBootFanoutExchange() {
        return new FanoutExchange(SPRING_BOOT_FANOUT_EXCHANGE);
    }

    @Bean
    public Queue springBootFanoutQueue1() {
        return new Queue(SPRING_BOOT_FANOUT_QUEUE1);
    }

    @Bean
    public Queue springBootFanoutQueue2() {
        return new Queue(SPRING_BOOT_FANOUT_QUEUE2);
    }

    //Bind queue and switch
    @Bean
    public Binding bindingQueue1(Queue springBootFanoutQueue1, FanoutExchange springBootFanoutExchange) {
        return BindingBuilder.bind(springBootFanoutQueue1).to(springBootFanoutExchange);
    }

    @Bean
    public Binding bindingQueue2(Queue springBootFanoutQueue2, FanoutExchange springBootFanoutExchange) {
        return BindingBuilder.bind(springBootFanoutQueue2).to(springBootFanoutExchange);
    }


    /**
     * Routing mode
     * Declare Direct type switches
     */
    @Bean
    public DirectExchange springBootDirectExchange() {
        return new DirectExchange(SPRING_BOOT_DIRECT_EXCHANGE);
    }

    @Bean
    public Queue springBootDirectQueue1() {
        return new Queue(SPRING_BOOT_DIRECT_QUEUE1);
    }

    @Bean
    public Queue springBootDirectQueue2() {
        return new Queue(SPRING_BOOT_DIRECT_QUEUE2);
    }

    //Bind queue and switch
    @Bean
    public Binding bindingQueue3(Queue springBootDirectQueue1, DirectExchange springBootDirectExchange) {
        return BindingBuilder.bind(springBootDirectQueue1).to(springBootDirectExchange).with("debug");
    }

    @Bean
    public Binding bindingQueue4(Queue springBootDirectQueue2, DirectExchange springBootDirectExchange) {
        return BindingBuilder.bind(springBootDirectQueue2).to(springBootDirectExchange).with("info");
    }


    /**
     * wildcard pattern 
     * Declare Topic type switch
     */
    @Bean
    public TopicExchange springBootTopicExchange() {
        return new TopicExchange(SPRING_BOOT_TOPIC_EXCHANGE);
    }

    @Bean
    public Queue springBootTopicQueue1() {
        return new Queue(SPRING_BOOT_TOPIC_QUEUE1);
    }

    @Bean
    public Queue springBootTopicQueue2() {
        return new Queue(SPRING_BOOT_TOPIC_QUEUE2);
    }

    //Bind queue and switch
    @Bean
    public Binding bindingQueue5(Queue springBootTopicQueue1, TopicExchange springBootTopicExchange) {
        return BindingBuilder.bind(springBootTopicQueue1).to(springBootTopicExchange).with("java.*");
    }

    @Bean
    public Binding bindingQueue6(Queue springBootTopicQueue2, TopicExchange springBootTopicExchange) {
        return BindingBuilder.bind(springBootTopicQueue2).to(springBootTopicExchange).with("java.#");
    }
}

6.3. Building consumer Engineering

6.3.1 create project

Create springboot rabbitmq consumer for consumer Engineering

6.3.2. Add dependency

<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.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

6.2.3 configuration file

Create application.yml as follows

server:
  port: 9090
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /

6.2.4 message listening and processing class

Write message listener MyListener

@Component
public class MyListener {
    /**
     * Listen for messages from a queue
     *
     * @param message Received message
     */
    @RabbitListener(queues = "spring_boot_simple_queue")
    public void simpleQueueListener(String message) {
        System.out.println("simpleQueue queue: The message received by the consumer is:" + message);
    }

    @RabbitListener(queues = "spring_boot_work_queue")
    public void workQueueListener1(String message) {
        System.out.println("workQueue Queue 1: The message received by the consumer is:" + message);
    }

    @RabbitListener(queues = "spring_boot_work_queue")
    public void workQueueListener2(String message) {
        System.out.println("workQueue: The message received by the consumer is:" + message);
    }

    @RabbitListener(queues = "spring_boot_fanout_queue1")
    public void fanoutQueueListener1(String message) {
        System.out.println("fanout Queue 1: The message received by the consumer is:" + message);
    }

    @RabbitListener(queues = "spring_boot_fanout_queue2")
    public void fanoutQueueListener2(String message) {
        System.out.println("fanout Queue 2: The message received by the consumer is:" + message);
    }

   /* @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "spring_boot_direct_queue1"),
            exchange = @Exchange(name = "spring_boot_direct_exchange", type = ExchangeTypes.DIRECT),
            key = {"debug", "info"}
    ))
    public void listenDirectQueue1(String msg) {
        System.out.println("Consumer receives debug and info messages: ["+ msg +"] ");
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "spring_boot_direct_queue2"),
            exchange = @Exchange(name = "spring_boot_direct_exchange", type = ExchangeTypes.DIRECT),
            key = {"debug"}
    ))
    public void listenDirectQueue2(String msg) {
        System.out.println("The consumer receives a debug message: ["+ msg +"] ");
    }*/

    @RabbitListener(queues = "spring_boot_direct_queue1")
    public void listenDirectQueue1(String message) {
        System.out.println("Consumer 1 received debug and info Message:" + message);
    }

    @RabbitListener(queues = "spring_boot_direct_queue2")
    public void listenDirectQueue2(String message) {
        System.out.println("Consumer 2 received debug Message:" + message);
    }

    @RabbitListener(queues = "spring_boot_topic_queue1")
    public void listenTopicQueue1(String message) {
        System.out.println("Consumer 1 received java perhaps java.x java.xx...Message:" + message);
    }

    @RabbitListener(queues = "spring_boot_topic_queue2")
    public void listenTopicQueue2(String message) {
        System.out.println("Consumer 2 received java.x Message:" + message);
    }
}

6.4 test

@SpringBootTest
class SpringbootRabbitmqProducerApplicationTests {

    // Simple queue
    public static final String SPRING_BOOT_SIMPLE_QUEUE = "spring_boot_simple_queue";
    // Work queue
    public static final String SPRING_BOOT_WORK_QUEUE = "spring_boot_work_queue";
    // Broadcast mode
    public static final String SPRING_BOOT_FANOUT_EXCHANGE = "spring_boot_fanout_exchange";
    //Routing mode
    public static final String SPRING_BOOT_DIRECT_EXCHANGE = "spring_boot_direct_exchange";
    //wildcard pattern 
    public static final String SPRING_BOOT_TOPIC_EXCHANGE = "spring_boot_topic_exchange";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSimpleQueue() {
        // news
        String message = "hello, spring amqp!";
        // send message
        rabbitTemplate.convertAndSend(SPRING_BOOT_SIMPLE_QUEUE, message);
    }

    @Test
    public void testWorkQueue() {
        // news
        String message = "hello, spring amqp!";
        for (int i = 1; i < 20; i++) {
            // send message
            rabbitTemplate.convertAndSend(SPRING_BOOT_WORK_QUEUE, message + "======" + i);
        }
    }

    @Test
    public void testFanoutExchange() {
        // news
        String message = "hello, spring boot, I am fanout Switch";
        for (int i = 1; i <= 20; i++) {
            // send message
            rabbitTemplate.convertAndSend(SPRING_BOOT_FANOUT_EXCHANGE, "", message + "======" + i);
        }
    }

    @Test
    public void testDirectExchange() {
        // news
        String message = "hello, spring boot, I am direct Switch";
        for (int i = 1; i <= 20; i++) {
            // send message
            if (i % 2 == 0) {
                rabbitTemplate.convertAndSend(SPRING_BOOT_DIRECT_EXCHANGE, "debug", message + i);
            } else {
                rabbitTemplate.convertAndSend(SPRING_BOOT_DIRECT_EXCHANGE, "info", message + i);

            }
        }
    }

    @Test
    public void testTopicExchange() {
        // news
        String message = "hello, spring boot, I am topic Switch";
        for (int i = 1; i <= 20; i++) {
            // send message
            if (i % 2 == 0) {
                rabbitTemplate.convertAndSend(SPRING_BOOT_TOPIC_EXCHANGE, "java.php", message + i);
            } else {
                rabbitTemplate.convertAndSend(SPRING_BOOT_TOPIC_EXCHANGE, "java.go.python", message + i);

            }
        }
    }
}

For the first startup, the producer should be started first to create queues and switches. If the consumer is started first, the queue cannot be found

7, RabbitMQ advanced features

7.1 message reliability delivery

When using RabbitMQ, the message sender wants to eliminate any message loss or delivery failure scenarios. RabbitMQ provides us with two ways to control the delivery reliability mode of messages.

  • confirm confirmation mode
  • Return return mode

rabbitmq the delivery path of the whole message is:
producer —> rabbitmq broker —> exchange —> queue —> consumer

  • If the message is from producer to exchange, a confirmCallback will be returned.
  • If the message fails to be delivered from exchange to queue, a returnCallback will be returned.

We will use these two callback s to control the reliable delivery of messages

7.1.1. confirm mode code implementation

1. Create maven project, message producer project, project module name: rabbitmq producer spring
2. Add dependency

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.13</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.13</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>2.1.8.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
    <dependency>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

3. Create a rabbitmq.properties configuration file in the resources directory and add information related to the link RabbitMQ

rabbitmq.host=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtual-host=/

4. Create the spring-rabbitmq-producer.xml configuration file in the resources directory and add the following configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbitmq="http://www.springframework.org/schema/rabbit"
       xmlns:rabbitm="http://www.springframework.org/schema/rabbit"
       xmlns:rabbit="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/rabbit
                           http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!--Load profile-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!--
        definition rabbitmq connectionFactory
        1.set up publisher-confirms="true"
    -->
    <rabbitm:connection-factory id="connectionFactory"
                                host="${rabbitmq.host}"
                                port="${rabbitmq.port}"
                                username="${rabbitmq.username}"
                                password="${rabbitmq.password}"
                                virtual-host="${rabbitmq.virtual-host}"
                                publisher-confirms="true"/>

    <!--Define management switches, queues-->
    <rabbitmq:admin connection-factory="connectionFactory"/>

    <!--definition rabbitmqTemplate Object operation can easily send messages in code-->
    <rabbitmq:template id="rabbitTemplate" connection-factory="connectionFactory"/>

    <!--2.Message reliability delivery (production side)-->
    <rabbitmq:queue id="queue_confirm" name="queue_confirm"/>

    <rabbitmq:direct-exchange name="exchange_confirm">
        <rabbitmq:bindings>
            <rabbitmq:binding queue="queue_confirm" key="confirm"></rabbitmq:binding>
        </rabbitmq:bindings>
    </rabbitmq:direct-exchange>
</beans>

5. Write test code

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * Confirmation mode:
     * Steps:
     * 1. Enable confirmation mode: enable publisher ‐ confirms="true" in ConnectionFactory
     * 2. Define the ConfirmCallBack callback function in rabbitTemplate
     */
    @Test
    public void testConfirm() {
        /**
         *
         * confirm(@NonNull CorrelationData correlationData, boolean ack, @Nullable String cause)
         * correlationData: Related configuration information
         * ack: exchange Whether the switch successfully received the message. true means success and false means failure
         * cause: Failure reason
         */
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm Method was executed....");
                if (ack) {
                    //Received successfully
                    System.out.println("Receive success message" + cause);
                } else {
                    //Receive failed
                    System.out.println("Receive failure message" + cause);
                    //Failure processing, let the message be sent again.
                }
            }
        });

        // 3. Send message
        //rabbitTemplate.convertAndSend("exchange_confirm", "confirm", "test confirm message...);
      	rabbitTemplate.convertAndSend("exchange_confirm2222", "confirm", "test confirm news......");
    }
}

6. Test results

7.1.2 return return mode code implementation

Fallback mode: when the message is sent to Exchange and the Exchange fails to route to the Queue, the ReturnCallBack will be executed. The specific implementation is as follows:

  1. In the spring-rabbitmq-producer.xml configuration file, add the configuration in the rabbit: connection factory node:
<rabbitm:connection-factory id="connectionFactory"
                            host="${rabbitmq.host}"
                            port="${rabbitmq.port}"
                            username="${rabbitmq.username}"
                            password="${rabbitmq.password}"
                            virtual-host="${rabbitmq.virtual-host}"
                            publisher-returns="true"
                            publisher-confirms="true"/>
  1. Writing test methods
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

	...
	
    /**
     * Steps:
     * 1. Enable fallback mode: publisher ‐ returns="true"
     * 2. Set ReturnCallBack
     * 3. Set the mode in which Exchange processes messages:
     * 1. If the message is not routed to the Queue, the message is discarded (default)
     * 2. If the message is not routed to the Queue, it is returned to the message sender ReturnCallBack
     */
    @Test
    public void testReturn() {
        //Sets the mode in which the switch processes failure messages
        rabbitTemplate.setMandatory(true);
        //2. Set ReturnCallBack
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * message: Message object
             * replyCode:  Error code
             * replyText:  error message
             * exchange:  Switch
             * routingKey:  Routing key
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return Yes....");
                System.out.println(message);
                System.out.println(replyCode);
                System.out.println(replyText);
                System.out.println(exchange);
                System.out.println(routingKey);
                //handle
            }
        });

        //3. Send a message, set the routingKey as a key that does not conform to the rules, and observe the console print results.
        // rabbitTemplate.convertAndSend("exchange_confirm", "confirm", "message confirm....");
       rabbitTemplate.convertAndSend("exchange_confirm", "confirm222", "message confirm....");
    }
}

7.1.3 summary

For confirmation mode:

  • Set publisher confirms = "true" of ConnectionFactory to enable confirmation mode.
  • Use rabbitTemplate.setConfirmCallback to set the callback function. When the message is sent to exchange, the confirm method is called back. Judge the ack in the method. If it is true, the sending succeeds. If it is false, the sending fails and needs to be processed.

For return mode

  • Set publisher returns = "true" of ConnectionFactory to enable return mode.
  • Use rabbitTemplate.setReturnCallback to set the return function. When the message fails to be routed from exchange to queue, if the rabbitTemplate.setMandatory(true) parameter is set, the message will be returned to the producer. And execute the callback letter returnedMessage.

The transaction mechanism is also provided in RabbitMQ, but the performance is poor, so it will not be explained here.
Use the channel column method to complete transaction control:
txSelect() is used to set the current channel to transaction mode
txCommit(), used to commit transactions
txRollback(), used to roll back transactions

7.2,Consumer ACK

ack refers to the knowledge, which is confirmed. Indicates the confirmation method after the consumer receives the message.
There are three confirmation methods:

  • Automatic confirmation: acknowledge = "none"
  • Manual confirmation: acknowledge = "manual"
  • Confirm according to the abnormal conditions: acknowledge = "auto", (this method is troublesome and will not be explained)

Automatic acknowledgement means that once a message is received by the Consumer, it is automatically acknowledged and the corresponding message is removed from the message cache of RabbitMQ. However, in the actual business processing, it is likely that the message will be lost if an exception occurs in the business processing after the message is received.

If a manual confirmation is set, you need to call channel.basicAck() manually after the transaction is successful. If there is an exception, call the channel.basicNack() method to send the message automatically.

7.2.1 code implementation

1. Create maven project, message consumer project, project module name: rabbitmq consumer spring
2. Add dependency

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.13</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.13</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>2.1.8.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
    <dependency>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

3. Create a rabbitmq.properties configuration file in the resources directory and add information related to the link RabbitMQ

rabbitmq.host=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.virtual-host=/

4. Create the spring-rabbitmq-consumer.xml configuration file in the resources directory and add the following configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--Load profile-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- definition rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>


    <!--Packet scanning-->
    <context:component-scan base-package="com.java521.listener"/>

    <!--Define listener container-->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
        <rabbit:listener ref="ackListener" queue-names="queue_confirm"></rabbit:listener>
    </rabbit:listener-container>
</beans>

5. Write the ackListener listening class to implement the ChannelAwareMessageListener interface

/**
 * Consumer ACK Mechanism:
 * 1. Set manual sign in. acknowledge="manual"
 * 2. Let the listener class implement the ChannelAwareMessageListener interface
 * 3. If the message is processed successfully, basicAck() of channel is called to sign in
 * 4. If the message processing fails, call channel's basicNack() to refuse signing, and the broker sends it to the consumer again
 */
@Component
public class AckListener implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //1. Receive and send messages
            System.out.println(new String(message.getBody()));

            //2. Processing logical business
            System.out.println("Processing logical business");
            //Manual throwing exception
            int i = 1 / 0;

            //3. Manual sign in
            channel.basicAck(deliveryTag, true);
        } catch (Exception e) {
            //e.printStackTrace();
            /**
             * 4.Refuse to sign
             * basicNack(long deliveryTag, boolean multiple, boolean requeue)
             * deliveryTag: The number of the message, which is a 64 bit long integer value
             * multiple: Parameter controls single rejection and batch rejection,
             *          true: Reject all unacknowledged messages before the deliveryTag number
             *          false: Reject this message numbered deliveryTag
             * requeue: Go back to the queue.
             *          true: When the message returns to the queue, the broker will resend the message to the consumer
             *          false: Rabbit MQ Messages are immediately removed from the queue and not sent to new consumers.
             */
            channel.basicNack(deliveryTag, true, true);
        }
    }
}

7.2.2 summary

Set the acknowledge attribute in the rabbit: listener container tag and set the ack mode

  • none: automatic confirmation,
  • Manual: manual confirmation

If there is no exception on the consumer side, call channel.basicAck(deliveryTag,false); Method to confirm the sign in message
If there is an exception, call basicNack or basicReject in catch, reject the message, and let MQ resend the message.

How to ensure high reliable transmission of messages

  1. Persistence
    exchange to be persistent
    queue to be persistent
    message to persist
  2. Confirm by the manufacturer
  3. Consumer confirms Ack
  4. Broker high availability

7.3 current limiting at consumer end

As shown in the figure above: if the relevant business functions need to be maintained in system A, the service of system A may need to be stopped. At this time, the message producer will always send messages to be processed in MQ. At this time, the service of the consumer has been closed, resulting in A large number of messages accumulating in MQ. If system A is successfully started, by default, the message consumers will pull all the messages accumulated in MQ to their own services at one time, resulting in the service processing A large number of businesses in A short time, which may lead to the collapse of the system service. Therefore, it is very necessary to limit the current at the consumer end.
You can use the listener container configuration property perfetch = 1 in MQ to indicate that the consumer will pull a message from MQ to consume each time. The next message will not be pulled until the consumption is manually confirmed.

7.3.1 code implementation

  1. Write the QosListener listening class to ensure that the current message processing mechanism of listening class is ACK in manual mode
@Component
public class QosListener implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        Thread.sleep(1000);
        //1. Get message
        System.out.println(new String(message.getBody()));
        //2. Processing business logic
        //3. Signature
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
    }
}
  1. Add a configuration in the listener container configuration property of the configuration file
<!--Define listener container-->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="1">
<!--        <rabbit:listener ref="ackListener" queue-names="queue_confirm"></rabbit:listener>-->
    <rabbit:listener ref="qosListener" queue-names="queue_confirm"></rabbit:listener>
</rabbit:listener-container>

Configuration Description:
perfetch = 1, which means that the consumer will pull a message from mq to consume each time, and will not continue to pull the next message until the consumption is manually confirmed.

7.3.2 summary

Configure the prefetch attribute in rabbit: listener container to set how many messages the consumer pulls at a time. The confirmation mode of the consumer must be manual confirmation. acknowledge=“manual”

7.4,TTL

The full name of TTL is Time To Live. When the message reaches the lifetime and has not been consumed, it will be automatically cleared.
RabbitMQ can set the expiration time for messages or for the entire Queue.

You can set the expiration time in the RabbitMQ administrative console.

7.4.1 code implementation

7.4.1.1. Set the expiration time of the queue

  1. In the message producer, add the following configuration in the spring-rabbitmq-producer.xml configuration file
<!--ttl-->
<rabbitmq:queue name="queue_ttl" id="queue_ttl">
    <!--set up queue Parameters of-->
    <rabbitmq:queue-arguments>
        <!--x‐message‐ttl Refers to the expiration time of the queue-->
        <entry key="x-message-ttl" value="30000" value-type="java.lang.Integer"></entry>
    </rabbitmq:queue-arguments>
</rabbitmq:queue>
<rabbitmq:topic-exchange name="exchange_ttl">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="ttl.#" queue="queue_ttl"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>
  1. Write test methods for sending messages
@Test
public void testTtl() {
    for (int i = 0; i < 10; i++) {
        // send message
        rabbitTemplate.convertAndSend("exchange_ttl", "ttl.hehe", "message ttl...." + i);
    }
}

Test result: after the message is sent successfully, you will see the message on the management console of RabbitMQ after 30s, and the message will be deleted automatically.

7.4.1.2. Set the expiration time of a single message

Write code to test, and set the expiration time of the queue to 30s and the expiration time of a single message to 3s

@Test
public void testTtl2() {
    // The message post-processing object sets some message parameter information
    MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
        @Override
        public Message postProcessMessage(Message message) throws AmqpException {
            //1. Set message
            message.getMessageProperties().setExpiration("3000");//Expiration time of the message
            System.out.println("get into");
            //2. Return the message
            return message;
        }
    };

    //Messages expire individually
    //rabbitTemplate.convertAndSend("exchange_ttl", "ttl.hehe", "message ttl....", messagePostProcessor);

    for (int i = 1; i <= 10; i++) {
        if (i == 5) {
            System.out.println("implement...");
            //Messages expire individually
            rabbitTemplate.convertAndSend("exchange_ttl", "ttl.hehe", "message ttl...." + i, messagePostProcessor);
        } else {
            //Messages that do not expire
            rabbitTemplate.convertAndSend("exchange_ttl", "ttl.hehe" + i, "message ttl...." + i);
        }
    }
}

If the expiration time of the message is set, the expiration time of the queue is also set, whichever is shorter.

  • When the queue expires, all messages in the queue are removed.
  • After a message expires, only when the message is at the top of the queue will it be judged whether it has expired (removed)

7.4.2 summary

  • Set the usage parameter of queue expiration time: x-message-ttl, unit: ms, which will uniformly expire the messages of the whole queue.
  • Set the expiration time of the message and use the parameter: expiration. Unit: ms (milliseconds). When the message is at the head of the queue (consumption), it will judge whether the message has expired.
  • If both are set, whichever is shorter.

7.5 dead letter queue

Dead letter queue, English abbreviation: DLX. Dead Letter Exchange (Dead Letter Exchange). When the message becomes Dead message, it can be re sent to another switch, which is DLX.

There are three situations when a message becomes a dead letter

  1. The queue message length reaches the limit (the queue message to be delivered is full and cannot be delivered)
  2. The consumer rejects the consumption message, basicNack/basicReject, and does not put the message back into the original target queue, request = false;
  3. The message expiration setting exists in the original queue, and the message arrival timeout is not consumed;

If the queue containing dead letter is configured with the Dead Letter Exchange attribute and a switch is specified, the dead letter in the queue will be delivered to the switch, which is called Dead Letter Exchange (DLX).

Queue bound dead letter switch:
Set parameters for the queue: x-dead-letter-exchange and x-dead-letter-routing-key

When the queue delivers a dead letter to the dead letter switch, it must know two information:

  • Dead letter switch name
  • RoutingKey bound between dead letter switch and dead letter queue

In this way, it can ensure that the delivered messages can reach the dead letter switch and correctly route to the dead letter queue.

7.5.1 code implementation

1. In the message producer, add the following configuration in the spring-rabbitmq-producer.xml configuration file:

Declare normal queues (test_queue_dlx) and switches (test_exchange_dlx)

<rabbitmq:queue name="test_ueue_dlx" id="test_queue_dlx"></rabbitmq:queue>
<rabbitmq:topic-exchange name="test_exchange_dlx">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="test.dlx.#" queue="test_queue_dlx"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>

Declare dead letter queue (queue_dlx) and dead letter exchange (exchange_dlx)

<rabbitmq:queue name="queue_dlx" id="queue_dlx"></rabbitmq:queue>
<rabbitmq:topic-exchange name="exchange_dlx">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="dlx.#" queue="queue_dlx"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>

Bind the dead letter switch to the normal queue and set relevant parameter information

<!--
   Dead letter queue:
       1. Declare a normal queue(test_queue_dlx)And switches(test_exchange_dlx)
       2. Declare dead letter queue(queue_dlx)Dead letter switch(exchange_dlx)
       3. Normal queue binding dead letter switch
           Set two parameters:
               * x-dead-letter-exchange: Dead letter switch name
               * x-dead-letter-routing-key: Sent to dead letter exchange routingkey
 -->

<rabbitmq:queue name="test_queue_dlx" id="test_queue_dlx">
    <!--3. Normal queue binding dead letter switch-->
    <rabbitmq:queue-arguments>
        <!--3.1 x-dead-letter-exchange: Dead letter switch name-->
        <entry key="x-dead-letter-exchange" value="exchange_dlx" />

        <!--3.2 x-dead-letter-routing-key: Sent to dead letter exchange routingkey-->
        <entry key="x-dead-letter-routing-key" value="dlx.hehe" />

        <!--4.1 Set the expiration time of the queue ttl-->
        <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer" />
        <!--4.2 Set the length limit of the queue max-length -->
        <entry key="x-max-length" value="10" value-type="java.lang.Integer" />
    </rabbitmq:queue-arguments>
</rabbitmq:queue>
<rabbitmq:topic-exchange name="test_exchange_dlx">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="test.dlx.#" queue="test_queue_dlx"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>


<!--
   2. Declare dead letter queue(queue_dlx)Dead letter switch(exchange_dlx)
-->

<rabbitmq:queue name="queue_dlx" id="queue_dlx"></rabbitmq:queue>
<rabbitmq:topic-exchange name="exchange_dlx">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="dlx.#" queue="queue_dlx"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>

2. Writing test methods

/**
 * Send test dead letter message:
 * 1. Expiration time
 * 2. Length limit
 * 3. Message rejection
 */
@Test
public void testDlx() {
    //1. Test expiration time, dead letter message
    rabbitTemplate.convertAndSend("test_exchange_dlx", "test.dlx.haha", "I'm a message. Will I die?");
    //2. After testing the length limit, the message is dead letter
    for (int i = 0; i < 20; i++) {
        rabbitTemplate.convertAndSend("test_exchange_dlx", "test.dlx.haha", "I'm a message. Will I die?");
    }
    //3. Test message rejection
    rabbitTemplate.convertAndSend("test_exchange_dlx", "test.dlx.haha", "I'm a message. Will I die?");
}

7.5.2 summary

  1. Dead letter switch and dead letter queue are no different from ordinary ones
  2. When a message becomes a dead letter, if the queue is bound to a dead letter switch, the message will be rerouted to the dead letter queue by the dead letter switch
  3. There are three situations when a message becomes a dead letter:
    The queue message length reaches the limit;
    The consumer rejects the consumption message and does not return to the queue;
    The message expiration setting exists in the original queue, and the message arrival timeout is not consumed;

7.6 delay queue

Delay queue, that is, messages will not be consumed immediately after entering the queue, but only after reaching the specified time.
Propose requirements:

  1. If the order is not paid within 30 minutes after placing the order, cancel the order and roll back the inventory.
  2. After 7 days of successful registration of new users, send SMS greetings.
    Implementation method:
  3. timer
  4. Delay queue

Note: the delay queue function is not provided in RabbitMQ.
However, TTL + dead letter queue combination can be used to achieve the effect of delay queue.

7.6.1 code implementation

1. In the message producer, add the following configuration in the spring-rabbitmq-producer.xml configuration file:

<!-- 1. Define normal switch( order_exchange)And queue(order_queue)-->
<rabbitmq:queue id="order_queue" name="order_queue">
    <!-- 3. Bind and set the normal queue expiration time to 30 minutes -->
    <rabbitmq:queue-arguments>
        <entry key="x-dead-letter-exchange" value="order_exchange_dlx"/>
        <entry key="x-dead-letter-routing-key" value="dlx.order.cancel"/>
        <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"/>
    </rabbitmq:queue-arguments>
</rabbitmq:queue>

<rabbitmq:topic-exchange name="order_exchange">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="order.#" queue="order_queue"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>


<!-- 2. Define dead letter switch( order_exchange_dlx)And queue(order_queue_dlx)-->
<rabbitmq:queue id="order_queue_dlx" name="order_queue_dlx"></rabbitmq:queue>

<rabbitmq:topic-exchange name="order_exchange_dlx">
    <rabbitmq:bindings>
        <rabbitmq:binding pattern="dlx.order.#" queue="order_queue_dlx"></rabbitmq:binding>
    </rabbitmq:bindings>
</rabbitmq:topic-exchange>

2. Writing test methods

@Test
public void testDelay() throws InterruptedException {
    //1. Send the order message. In the future, the order system will send the message after the order is placed successfully
    rabbitTemplate.convertAndSend("order_exchange", "order.msg", "Order information: id=1,time=2021 year");
    //2. Print countdown 10 seconds
    for (int i = 10; i > 0; i--) {
        System.out.println(i + "...");
        Thread.sleep(1000);
    }
}

7.6.2 summary

  1. Delay queue means that after a message enters the queue, it can be delayed for a certain time before consumption.
  2. RabbitMQ does not provide the function of delay queue, but you can use: TTL + DLX to achieve the effect of delay queue.

7.7 log and monitoring

7.7.1 RabbitMQ log

RabbitMQ default log storage path: / var/log/rabbitmq/rabbit@xxx.log Or install the directory / var/log/rabbitmq/rabbit@xxx.log

RabbitMQ log details:
The log contains RabbitMQ version number, Erlang version number, RabbitMQ service node name, hash value of cookie, RabbitMQ configuration file address, memory limit, disk limit, creation of default account guest, permission configuration, etc.

7.7.2 web control console monitoring

Directly access the current IP address: 15672 and enter the user name and password (guest by default) to view the management console of RabbitMQ. Of course, you can also view it in the form of commands as follows:

## View queue
rabbitmqctl list_queues

## View user
rabbitmqctl list_users

## View connections
rabbitmqctl list_connections

## Other related commands (understand):
## View exchanges
rabbitmqctl list_exchanges

## View consumer information
rabbitmqctl list_consumers

## View environment variables
rabbitmqctl environment

## View unacknowledged queues
rabbitmqctl list_queues name messages_unacknowledged

## View memory usage for a single queue
rabbitmqctl list_queues name memory

## View ready queues
rabbitmqctl list_queues name messages_ready

7.8 message tracking

In the process of using any message middleware, it is inevitable that a message will be lost abnormally. For RabbitMQ, it may be because the producer or consumer has disconnected from RabbitMQ, and they use different confirmation mechanisms with RabbitMQ; it may also be because of different forwarding strategies between the switch and the queue; or even the switch does not communicate with any queue In addition, RabbitMQ's own clustering strategy may also lead to message loss. At this time, a better mechanism is needed to track and record the message delivery process, so as to assist the development and operation and maintenance personnel in locating the problem.

In RabbitMQ, the functions of Firehose and rabbitmq_tracing plug-ins can be used to realize message tracing.

7.8.1 message tracking - Firehose

The mechanism of firehouse is to send messages delivered by producers to rabbitmq, and messages delivered by rabbitmq to consumers to the default exchange according to the specified format. The name of this default exchange is amq.rabbitmq.trace, which is an exchange of topic type. The routing key of messages sent to this exchange is publish.exchangename and deliver.queuename. Where ex Changename and queuename are the names of the actual exchange and queue, corresponding to the messages delivered by the producer to the exchange and the messages obtained by the consumer from the queue.

Note: opening trace will affect the message writing function. Please close it after opening it properly.

## Turn on the Firehose command
rabbitmqctl trace_on

Message tracking verification:
1. Create a queue test_trace, bind the current queue to the amq.rabbitmq.trace switch, and set the RoutingKey to:#


2. Before message tracking is turned on, we send a message

After the current message is successfully sent, we can see the specific information of the current message on the console
3. Set to enable message tracking when sending a message

We found that the current message also exists normally. After message tracking is enabled, one more message will be sent to the current queue by amq.rabbitmq.trace switch. The content of the message is relatively complete.

Suggestion: we can turn on message tracking in the development stage and turn it off in the actual production environment
Close Firehose command: rabbitmqctl trace_off

7.8.2 message tracking -rabbitmq_tracking

Rabbitmq_tracing is the same as Firehose in implementation, but rabbitmq_tracing has a layer more GUI packaging than Firehose, which is easier to use and manage.

## Enable plug-ins
rabbitmq-plugins enable rabbitmq_tracing

Create trace


After sending the message successfully, we click the log file and ask for the login user name and password of RabbitMQ.

Suggestion: we can start the message tracking plug-in in the development stage. It is not recommended to start it in the actual production environment, unless it is a very special business scenario. We can choose to start it according to the actual situation.

7.8.3 RabbitMQ application problems

7.8.3.1 message reliability guarantee

Put forward requirements: how to ensure that 100% of messages are sent successfully?
First of all, we should make it clear that no system can guarantee 100% successful delivery of messages. We can ensure that messages are sent to the target party with the highest and most reliable quality.
The message supplement mechanism is adopted in RabbitMQ to ensure the reliability of messages

Step analysis:
Participants: Message producer, message consumer, database, three queues (Q1, Q2, Q3), switch, callback check service, and scheduled check service

  1. The producer of the message stores the business data in the database
  2. Send message to queue Q1
  3. After waiting for a certain time, the message producer sends a delay message to queue Q3
  4. The consumer of the message listens to the Q1 queue message and receives it successfully
  5. The consumer of the message sends a confirmation message to queue Q2
  6. The callback check service listens to the confirmation message sent by the queue Q2
  7. After receiving the confirmation message, the callback check service writes the message to the database table of the message
  8. The callback check service will also listen to the queue Q3 delay message. If a message is received, it will compare the unique ID of the message with the database
  9. If no confirmation message is received, the callback check service will call the message producer remotely and resend the message
  10. Repeat steps 2-7 to ensure reliable message transmission
  11. If both the sending message and the delayed message are abnormal, the scheduled check service will monitor the message data in the message library. If inconsistent messages are found, the producer of the remote call message will resend the message.

7.8.3.2 message idempotency processing

Idempotency means that one and more requests for a resource should have the same result for the resource itself. That is, any multiple execution has the same impact on the resource itself as one execution.

In MQ, it refers to consuming multiple identical messages to get the same result as consuming the message once.

In this tutorial, the optimistic locking mechanism is used to ensure the idempotent operation of messages

Posted by GreenUser on Fri, 26 Nov 2021 01:21:21 -0800