RabbitMQ message queue - quick start

Table of contents

1. Introduction to MQ

1.1. What is MQ?

1.2. Problems that can be solved by MQ

1.2.1. Peak shaving and valley filling

1.2.3, asynchronous processing

1.3, MQ selection

1.3.1、Kafka

1.3.2、ActiveMQ

1.3.3、RocketMQ

1.3.4、RabbitMQ

2. Introduction to RabbitMQ

2.1. Overview of RabbitMQ

2.2、AMQP

2.3、JMS

2.4, RabbitMQ mode

3. Installation of RabbitMQ

3.1. Install Erlang

3.2. RabbitMQ installation

3.3. Create user

3.4, create a virtual host Virtual Hosts

4. RabbitMQ working mode

4.1. Simple mode

4.1.1. Producers

4.1.2. Consumers

4.2. Work queue mode (Work queues)

4.2.1. Producers 

4.2.2. Consumer 1

4.2.3. Consumer 2

​Edit 4.3, publish/subscribe mode (Publish/Subscribe)

4.3.1. Producers

4.3.2. Consumer 1

4.3.3. Consumer 2 

4.4, routing mode (Routing)

4.4.1. Producers

4.4.2. Consumer 1

4.4.3. Consumer 2

4.5. Wildcard mode (Topic)

4.5.1. Producers

4.5.1. Consumer 1

4.5.2. Consumer 2

4.6. Publisher Confirms

4.6.1, single confirmation

4.6.2, batch confirmation

4.6.3, asynchronous confirmation

4.6.4. Speed ​​comparison

4.7. Summary of RabbitMQ mode


1. Introduction to MQ

1.1. What is MQ?

From a literal point of view, it is essentially a queue, FIFO is first in, first out, but the content stored in the queue is just messages, and it is also a cross-process communication mechanism for upstream and downstream message transmission. In the Internet architecture, MQ is a very common upstream and downstream "logical decoupling + physical decoupling" message communication service. After using MQ, the message sending upstream only needs to rely on MQ, not on other services.

1.2. Problems that can be solved by MQ

1.2.1. Peak shaving and valley filling

Taking 12306 as an example, assuming that there are not many people who usually buy tickets, the QPS (query rate per second) of the order system is not very high, and only 1,000 requests are processed per second, but tickets may be rushed during holidays and Spring Festival travel seasons. There are a lot of people, and the amount of concurrency is much larger than usual. At this time, the order system obviously can't handle it. What to do, of course, we can design elastically scalable clusters to expand machine capacity to ensure high availability. But we can still use MQ to solve this problem.

The throughput of MQ is still strong, so we can design a highly available MQ, so that all requests go to MQ and cache them. In this way, the traffic and data during the peak period will be backlogged in MQ, and the traffic peak will be weakened (peak clipping ), and then our order system will avoid high concurrent requests, which can be slowly pulled from MQ Just deal with the news within your own ability. In this way, the backlog of news during the peak period will eventually be consumed, which can be called filling the valley.

1.2.2. Application decoupling

The product manager raised the demand, and many people paid attention to our 12306 WeChat client. We need to notify the WeChat applet after the ticket is successfully purchased. Then we need to modify the code of the order system. Once in a while is fine, if something like this happens once in a while, who can bear it?

One day, the SMS system was down, and then the customer successfully bought a ticket, but the SMS was not received, the email was not received, and the inventory was not deducted, which is fine. Your SMS system is broken, but my email system is fine. Why should it affect me and prevent customers from receiving emails? This is unreasonable. So, the coupling between the various systems is still too high, we should decouple. Didn't some people say that any problem on the Internet can be solved through a middleware, then let's see how MQ can help us solve this difficult thing.

Then we found that in fact, the SMS system and email system only rely on a piece of data generated by the order system, which is the order. Therefore, after the order system generates data, we send the order data to MQ, and return success, and then let SMS, email All other systems subscribe to MQ. Once they find that MQ has news, they will actively pull the message, and then analyze it for business processing. In this way, even if your SMS system is down, it will not affect other systems at all, and if you want to add a new system later, you don’t need to change the code of the order system, you just need to subscribe to the news provided by our MQ

1.2.3, asynchronous processing

Take the above 12306 as an example, assuming we don’t use MQ, then our code must be coupled together. After the order is placed successfully, we need to call these systems remotely through RPC in turn, and then wait for their response synchronously before returning to the user whether they are successful or not. result. Assuming that each system takes 200ms, then it takes 600ms

But in fact, sometimes we find that placing an order is a core business, and the pressure may be high. Customers are also anxious to know whether the order is successful, but notifications such as text messages and emails may not be urgent or caring at all, so why should we? Let these trivial businesses affect the efficiency of the core business, isn't it a bit of a waste of money. So we can design this logic to be asynchronous. After we can place an order successfully, we only need to send the order message to MQ, and then immediately return the result to notify the customer. This is the correct opening posture. In this way, my order system only needs to tell you MQ that I have placed an order successfully. After other modules receive the message, those who should send text messages will send text messages, and those who should send emails will send emails. Because the performance of MQ is very good, the efficiency is improved immediately.

1.3, MQ selection

1.3.1、Kafka

The main feature of Kafka is that it handles message consumption based on the pull mode and pursues high throughput. It was originally intended for log collection and transmission, and is suitable for data collection business of Internet services that generate a large amount of data. It is recommended for large companies to choose it. If there is a log collection function, kafka is definitely the first choice.

1.3.2、ActiveMQ

Advantages: stand-alone throughput of 10,000, timeliness of ms, high availability, based on master-slave architecture to achieve high availability, message reliability, and low probability of data loss.

Disadvantages: less maintenance, less use in high-throughput scenarios.

1.3.3、RocketMQ

Born for the financial Internet field, it requires high reliability scenarios, especially the order deduction in e-commerce, and business peak shaving. When a large number of transactions flood in, the backend may not be able to handle it in time. RocketMQ may be more trustworthy in terms of stability. These business scenarios have been tested many times in Ali Double 11. If your business has the above concurrent scenarios, it is recommended to choose RocketMQ.

1.3.4、RabbitMQ

Combined with the concurrency advantages of the erlang language itself, the performance is good, the timeliness is microsecond level, the community activity is relatively high, and the management interface is very convenient to use. If your data volume is not so large, small and medium-sized companies prefer RabbitMQ with relatively complete functions. This chapter takes RabbitMQ as an example.

2. Introduction to RabbitMQ

2.1. Overview of RabbitMQ

RabbitMQ is a message queue developed by the erlang language and implemented based on the AMQP protocol. It is a communication method between applications. Message queues are widely used in distributed system development.

RabbitMQ official address:

Messaging that just works — RabbitMQhttp://www.rabbitmq.com/

Architecture diagram of RabbitMQ 

2.2、AMQP

AMQP (Advanced Message Queuing Protocol) is a network protocol. It supports communication between a qualified client application (application) and a messaging middleware broker (messaging middleware broker). The main features are message-oriented, queuing, routing (including point-to-point and publish/subscribe), reliability, and security. AMQP is a protocol, similar to HTTP.

2.3、JMS

JMS is the Java Message Service (JavaMessage Service) application programming interface, which is an API for message-oriented middleware (MOM) in the Java platform, used to send messages between two applications or in a distributed system for asynchronous communication . JSM is an API interface specification, analogous to JDBC.

2.4, RabbitMQ mode

RabbitMQ provides 7 modes:

1. Simple mode

2, work-queue work queue mode

3. Publish/Subscribe publishing and subscription mode

4. Routing routing mode

5. Topics theme mode

6. RPC remote call mode (remote call, not too MQ, no introduction)

7. Publisher Confirms release confirmation

Introduction to the corresponding mode on the official website: RabbitMQ Tutorials — RabbitMQ

3. Installation of RabbitMQ

Official windows installation documentation, Installing on Windows — RabbitMQ

Notes on installation: 1. It is recommended to use the default installation path 2. The system user name must be in English

Baidu network disk resources

Link: https://pan.baidu.com/s/1C87Piy7co6BWf9v8N-4WrQ 
Extraction code: c17d

3.1. Install Erlang

RabbitMQ is developed by erlang language, so when I install RabbitMQ, I must install the Erlang environment first, pay attention to version matching

  • Right-click otp_win64_25.0.exe to run as administrator to install

  • Installing Erlang only requires the next step

3.2. RabbitMQ installation

  • Installing RabbitMQ only requires the next step

  • Right-click rabbitmq-server-3.10.10.exe to run as administrator to install

After installation, enter services.msc in cmd to open the service and open the RabbitMQ service

Find the sbin command directly through the windows shortcut key. If you do not find the sbin directory directly under the installation directory, run it as an administrator.

Enter the following command to start the management page. 

 rabbitmq-plugins.bat enable rabbitmq_management

After RabbitMQ is installed, you can visit http://localhost:15672 , which comes with the username and password of guest/guest.

If you still cannot access the page after performing the above operations, please restart the computer and test again.

3.3. Create user

 Role description:
1. The super administrator (administrator)
can log in to the management console, view all information, and operate users and policies.
2. The monitor (monitoring)
can log in to the management console, and at the same time can view the relevant information of the rabbitmq node (number of processes, memory usage, disk usage, etc.) 3. The
policymaker (policymaker)
can log in to the management console, and can Manage policies. But you cannot view the relevant information of the node (the part marked by the red box in the figure above).
4. Ordinary managers (management)
can only log in to the management console, but cannot see node information or manage policies.
5. Others
Unable to log in to the management console, usually ordinary producers and consumers.

3.4, create a virtual host Virtual Hosts

 Add virtual host permissions to users

4. RabbitMQ working mode

First create a Maven project

import dependencies

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

4.1. Simple mode

There is only one producer and one consumer; the producer sends messages to the queue, and the consumer gets messages from the queue.

Create a tool class to connect to RabbitMQ

public class ConnectionUtil {
    public static Connection getConnection() throws Exception {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //主机地址;默认为 localhost
        connectionFactory.setHost("localhost");
        //连接端口;默认为 5672
        connectionFactory.setPort(5672);
        //虚拟主机名称;默认为 /
        connectionFactory.setVirtualHost("/yh");
        //连接用户名;默认为guest
        connectionFactory.setUsername("guest");
        //连接密码;默认为guest
        connectionFactory.setPassword("guest");
        //创建连接
        return connectionFactory.newConnection();
    }
}

4.1.1. Producers

public class Producer {
    static final String QUEUE_NAME="simple_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();

        //声明队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接。只能有一个消费者监听到这队列
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        //发送信息
        String message="hello RabbitMQ";
        /**
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
        System.out.println("已发送信息:"+message);

        //关闭资源
        channel.close();
        connection.close();
    }
}

4.1.2. Consumers

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由的key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("接收到的消息:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

Start the consumer first, then the producer 

4.2. Work queue mode (Work queues)

Work QueuesCompared with the starter program 简单模式, there are one or more consumers, and multiple consumers consume messages in the same queue together.

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

4.2.1. Producers 

public class Producer {
    static final String QUEUE_NAME="simple_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();

        //声明队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接。只能有一个消费者监听到这队列
         * 参数4:是否在不
         * 使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        //发送信息
        for (int i = 1; i <=10 ; i++) {
            String message="hello RabbitMQ"+i;
            /**
             * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
             * 参数2:路由key,简单模式可以传递队列名称
             * 参数3:消息其它属性
             * 参数4:消息内容
             */
            channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
            System.out.println("已发送信息:"+message);
        }

        //关闭资源
        channel.close();
        connection.close();
    }
}

4.2.2. Consumer 1

public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //收到的消息
                    System.out.println("消费者1--接收到的消息:" + new String(body, "utf-8"));
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //监听消息
        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

4.2.3. Consumer 2

public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //收到的消息
                    System.out.println("消费者2--接收到的消息:" + new String(body, "utf-8"));
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        //监听消息
        channel.basicConsume(Producer.QUEUE_NAME, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

Start two consumers, and then start the producer to send messages; go to the console corresponding to the two consumers of IDEA to view the messages consumed by consumers.

4.3. Publish/Subscribe mode (Publish/Subscribe)

A message sent by a producer will be retrieved by multiple consumers. Messages sent to the Fanout Exchange will be forwarded to all Queues bound to the Exchange. This mode does not require any Routekey, and it is necessary to bind Exchange and Queue in advance. One Exchange can be bound to multiple Queues, and one Queue can be bound to multiple Exchanges. If the Exchange that receives the message is not bound to any Queue, the message will be lost.

Exchange: switch, X in the figure. On the one hand, receive messages sent by producers. On the other hand, knowing how to process the message, such as delivering it to a particular queue, delivering it to all queues, or discarding the message. How it works depends on the type of Exchange. There are three common types of Exchange:

  • Fanout: Broadcast, hand over the message to all queues bound to the exchange

  • Direct: directional, deliver the message to the queue that matches the specified routing key

  • Topic: wildcard, send the message to the queue that matches the routing pattern (routing pattern)

4.3.1. Producers

public class Producer {
    //交换机名称
    static final String FANOUT_EXCHANGE="fanout_exchange";
    //队列名称
    static final String FANOUT_QUEUE_1="fanout_queue_1";
    static final String FANOUT_QUEUE_2="fanout_queue_2";
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT);

        //发送信息
        for (int i = 1; i <=10 ; i++) {
            String message="发布/订阅模式--"+i;
            /**
             * 参数1:交换机名称,如果没有指定则使用默认Default Exchange
             * 参数2:路由key,简单模式可以传递队列名称
             * 参数3:消息其它属性
             * 参数4:消息内容
             */
            channel.basicPublish(FANOUT_EXCHANGE,"",null,message.getBytes());
            System.out.println("已发送信息:"+message);
        }

        //关闭资源
        channel.close();
        connection.close();
    }
}

4.3.2. Consumer 1

public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(Producer.FANOUT_EXCHANGE,BuiltinExchangeType.FANOUT);
        //声明队列
        channel.queueDeclare(Producer.FANOUT_QUEUE_1,true,false,false,null);
        //队列绑定交换机
        channel.queueBind(Producer.FANOUT_QUEUE_1,Producer.FANOUT_EXCHANGE,"");

        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //收到的消息
                    System.out.println("消费者1--接收到的消息:" + new String(body, "utf-8"));
            }
        };
        //监听消息
        channel.basicConsume(Producer.FANOUT_QUEUE_1, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

4.3.3. Consumer 2 

public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(Producer.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT);
        //声明队列
        channel.queueDeclare(Producer.FANOUT_QUEUE_2, true, false, false, null);
        //队列绑定交换机
        channel.queueBind(Producer.FANOUT_QUEUE_2, Producer.FANOUT_EXCHANGE,"");
        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //收到的消息
                System.out.println("消费者2--接收到的消息:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume(Producer.FANOUT_QUEUE_2, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

Start all consumers, and then use the producer to send messages; on the console corresponding to each consumer, you can view all the messages sent by the producer; reach the effect of broadcasting .

After executing the test code, actually go to the RabbitMQ management background to find Exchangesthe tab, fanout_exchangeclick the switch, and you can view the following bindings:

Note : When a message is sent to an exchange without queue binding, the message will be lost, because the exchange does not have the ability to store the message, and the message can only exist in the queue.

4.4, routing mode (Routing)

Any message sent to Direct Exchange will be forwarded to the Queue specified by RouteKey. In this mode, no binding operation is required for Exchange. A RouteKey is required for message delivery, which can be simply understood as the queue to be sent to name.

  • The binding between the queue and the switch cannot be arbitrary, but a RoutingKey(routing key) must be specified

  • The sender of the message must also specify the message ID when sending the message to Exchange RoutingKey.

  • Exchange no longer delivers messages to each bound queue, but Routing Keyjudges based on the message. Only when the queue is completelyRoutingkey consistent with the message will the message be received.Routing key

Publish/Subscribe发布与订阅模式The difference in coding is that the type of the switch is: Direct, and the routing key needs to be specified when the queue is bound to the switch.

4.4.1. Producers

public class Producer {
    //交换机名称
    static final String DIRECT_EXCHANGE = "direct_exchange";
    //队列名称
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();

        //声明交换机
        //  参数1:交换机名称
        //  参数2:交换机类型,fanout、topic、direct、headers
        channel.exchangeDeclare(DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT);

        // 发送信息到消费者1
        String message = "新增了商品。路由模式;routing key 为 insert ";
        channel.basicPublish(DIRECT_EXCHANGE, "insert", null, message.getBytes());
        System.out.println("已发送消息:" + message);

        // 发送信息到消费者2
        message = "修改了商品。路由模式;routing key 为 update";
        channel.basicPublish(DIRECT_EXCHANGE, "update", null, message.getBytes());
        System.out.println("已发送消息:" + message);

        //关闭资源
        channel.close();
        connection.close();
    }
}

4.4.2. Consumer 1

public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(Producer.DIRECT_EXCHANGE,BuiltinExchangeType.DIRECT);
        //声明队列
        channel.queueDeclare(Producer.DIRECT_QUEUE_INSERT,true,false,false,null);
        //队列绑定交换机
        channel.queueBind(Producer.DIRECT_QUEUE_INSERT, Producer.DIRECT_EXCHANGE,"insert");

        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消费者1-接收到的消息为:" + new String(body, "utf-8"));
            }
        };
        //监听消息
        channel.basicConsume(Producer.DIRECT_QUEUE_INSERT, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

4.4.3. Consumer 2

public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(Producer.DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT);
        //声明队列
        channel.queueDeclare(Producer.DIRECT_QUEUE_UPDATE, true, false, false, null);
        //队列绑定交换机
        channel.queueBind(Producer.DIRECT_QUEUE_UPDATE,Producer.DIRECT_EXCHANGE,"update");
        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消费者2-接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume(Producer.DIRECT_QUEUE_UPDATE, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

Start all consumers, and then use the producer to send messages; on the console corresponding to the consumer, you can see that the producer sends the message corresponding to the queue corresponding to the routing key; the effect is received as required .

After executing the test code, actually go to the RabbitMQ management background to find Exchangesthe tab, direct_exchangeclick the switch, and you can view the following bindings:

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

4.5. Wildcard mode (Topic)

Any message sent to Topic Exchange will be forwarded to all Queues that care about the topic specified by RouteKey. That is, each queue has its own concerned topic, and all messages have a title (RouteKey), and Exchange will forward the message to all concerned topics that can fuzzily match the RouteKey queue. This mode requires Routekey and binds Exchange and Queue in advance. A topic corresponding to the queue should be provided when binding. ' # ' means 0 or several keywords, ' * ' means one keyword. If Exchange does not find a Queue that can match the RouteKey, the message will be lost.

#: match one or more words

*: Match exactly 1 word

4.5.1. Producers

public class Producer {
    //交换机名称
    static final String TOPIC_EXCHANGE = "topic_exchange";
    //队列名称
    static final String TOPIC_QUEUE_1 = "topic_queue_1";
    static final String TOPIC_QUEUE_2 = "topic_queue_2";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        //  参数1:交换机名称
        //  参数2:交换机类型,fanout、topic、direct、headers
        channel.exchangeDeclare(TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC);
        // 发送信息
        String message = "新增了商品。Topic模式;routing key 为 item.insert " ;
        channel.basicPublish(TOPIC_EXCHANGE, "item.insert", null, message.getBytes());
        System.out.println("已发送消息:" + message);

        // 发送信息
        message = "修改了商品。Topic模式;routing key 为 item.update" ;
        channel.basicPublish(TOPIC_EXCHANGE, "item.update", null, message.getBytes());
        System.out.println("已发送消息:" + message);

        // 发送信息
        message = "删除了商品。Topic模式;routing key 为 item.delete" ;
        channel.basicPublish(TOPIC_EXCHANGE, "item.delete", null, message.getBytes());
        System.out.println("已发送消息:" + message);

        //关闭资源
        channel.close();
        connection.close();
    }
}

4.5.1. Consumer 1

public class Consumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(Producer.TOPIC_EXCHANGE,BuiltinExchangeType.TOPIC);
        //声明队列
        channel.queueDeclare(Producer.TOPIC_QUEUE_1,true,false,false,null);
        //队列绑定交换机
        channel.queueBind(Producer.TOPIC_QUEUE_1, Producer.TOPIC_EXCHANGE,"item.insert");
        channel.queueBind(Producer.TOPIC_QUEUE_1, Producer.TOPIC_EXCHANGE,"item.update");

        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //收到的消息
                System.out.println("消费者1-接收到的消息为:" + new String(body, "utf-8"));
            }
        };
        //监听消息
        channel.basicConsume(Producer.TOPIC_QUEUE_1, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

4.5.2. Consumer 2

public class Consumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(Producer.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC);
        //声明队列
        channel.queueDeclare(Producer.TOPIC_QUEUE_2, true, false, false, null);
        //队列绑定交换机
        channel.queueBind(Producer.TOPIC_QUEUE_2, Producer.TOPIC_EXCHANGE,"item.delete");
        //接收信息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //收到的消息
                System.out.println("消费者2-接收到的消息为:" + new String(body, "utf-8"));
            }
        };

        //监听消息
        channel.basicConsume(Producer.TOPIC_QUEUE_2, true, consumer);
        //不关闭资源,应该一直监听消息
    }
}

Start all consumers, and then use the producer to send messages; on the console corresponding to the consumer, you can see that the producer sends the message corresponding to the queue corresponding to the routing key; the effect is received as required ; and these routing keys can use wildcards.

After executing the test code, actually go to the RabbitMQ management background to find Exchangesthe tab, topic_exchangeclick the switch, and you can view the following bindings:

The Topic theme mode can Publish/Subscribe发布与订阅模式realize Routing路由模式the functions of and ; but the Topic can use wildcards when configuring the routing key, which is more flexible.

4.6. Publisher Confirms

Reliable release acknowledgment with publishers, which is a RabbitMQ extension that enables reliable releases. When publisher acknowledgment is enabled on a channel, RabbitMQ will asynchronously acknowledge messages published by the sender, meaning they are processed on the server side.

There are three strategies for release confirmation mode: 1. Single confirmation 2. Batch confirmation 3. Asynchronous confirmation

4.6.1, single confirmation

public class Single {
   //单个确认  267ms
   public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
       //获取连接
       Connection connection = ConnectionUtil.getConnection();
       //创建通道
       Channel channel = connection.createChannel();
       String queueName = UUID.randomUUID().toString();
       //声明队列
       channel.queueDeclare(queueName,true,false,false,null);
       //开启消息确认发布应答模式
       channel.confirmSelect();
       //记录开始时间
       long start = System.currentTimeMillis();
       //发送1000条信息
       for (int i = 1; i <=1000 ; i++) {
           //模拟信息
           String message=i+"";
           channel.basicPublish("",queueName,null,message.getBytes());
           //单个确认
           boolean flag = channel.waitForConfirms();
           if(flag){
               System.out.println("--------第"+(i)+"条信息发送成功!");
           }else{
               System.out.println("=========第"+(i)+"条消息发送失败!");
           }
       }
       //记录结束时间
       long end = System.currentTimeMillis();
       System.out.println("共耗时:"+(end-start)+"ms");
   }
}

4.6.2, batch confirmation

//批量确认  72ms
public class More {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        String queueName = UUID.randomUUID().toString();
        //声明队列
        channel.queueDeclare(queueName,true,false,false,null);
        //开启消息确认发布应答模式
        channel.confirmSelect();
        //记录开始时间
        long start = System.currentTimeMillis();
        //发送1000条信息
        for (int i = 1; i <=1000 ; i++) {
            //模拟信息
            String message=i+"";
            channel.basicPublish("",queueName,null,message.getBytes());
            //批量确认
            if(i%100==0){
                boolean flag = channel.waitForConfirms();
                if(flag){
                    System.out.println("--------第"+(i)+"条信息发送成功!");
                }else{
                    System.out.println("该批消息有确认失败的,需要重新发送整批失败的消息");
                }
            }

        }
        //记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("共耗时:"+(end-start)+"ms");
    }
}

4.6.3, asynchronous confirmation

//异步确认  46ms
public class Asny {
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        String queueName = UUID.randomUUID().toString();
        //声明队列
        channel.queueDeclare(queueName,true,false,false,null);
        //开启消息确认发布应答模式
        channel.confirmSelect();
        //消息发送成功回调函数
        ConfirmCallback ackCallback=(deliveryTag, multiple)->{
            System.out.println("消息发送成功");
        };
        //消息法送失败回调函数
        ConfirmCallback nackCallback=(deliveryTag, multiple)->{
            System.out.println("消息发送失败");
        };
        //注册监听器监听,异步通知
        channel.addConfirmListener(ackCallback,nackCallback);
        //记录开始时间
        long start = System.currentTimeMillis();
        //发送消息
        int message_num =1000;
        for (int i = 1; i <= message_num; i++) {
            String message=i+"";
            channel.basicPublish("",queueName,null,message.getBytes());
        }
        //记录发消息后时间
        long end=System.currentTimeMillis();
        System.out.println("该模式为异步批量确认模式:"+message_num+",耗时:"+(end-start)+"ms");
    }
}

4.6.4. Speed ​​comparison

  • Synchronous - publish messages individually: Synchronously wait for acknowledgment, simple, but very limited throughput.

  • Synchronization - batch release of messages: batch synchronization waits for confirmation, simple, reasonable throughput, once a problem occurs, it is difficult to deduce which message has a problem.

  • Asynchronous processing: Best performance and resource usage, fine control in case of errors, but slightly harder to implement.

4.7. Summary of RabbitMQ mode

  • Not direct Exchange switch (default switch)

    1. Simple simple mode: one producer, one consumer, the producer produces messages to a queue and is received by one consumer

    2. Work Queue work queue mode: one producer, multiple consumers (competitive relationship), the producer sends a message to a queue, which can be monitored by multiple consumers; a message can only be received by one consumer, and the consumer competition

  • Use Exchange switches; subscription mode (switches: broadcast fanout, directed direct, wildcard topic)

    1. Publish and subscribe mode: using a fanout broadcast type switch, a message can be sent to all queues bound to the switch

    2. Routing mode: using a direct directional switch, the consumption will carry the routing key, and the switch will compare the routing key of the message with the routing key of the queue. If they are consistent, the queue can receive the message

    3. Wildcard mode: use the switch of topic wildcard type, the consumption will carry the routing key (*, #), the switch compares the routing key of the message with the routing key of the queue, and if it matches, the queue can receive the message

  • Publisher Confirms

    1. Single confirmation

    2. Batch confirmation

    3. Synchronous processing

Guess you like

Origin blog.csdn.net/select_myname/article/details/128122436