头图

Write at the top

It’s been a long time since the last time I posted an article. In fact, I haven’t stopped writing during this period. I’m just busy looking for work and school closing. I re-organized the blog and will continue to update the article later. Updated~

  • This article is a bit long, so it is divided into two
  • PS: The article on Java Knowledge Questions and Answers on Github has not stopped writing, and it will be updated recently.

1. In a simple way

1.1 What is middleware?

Definition of IDC (Internet Data Center): Middleware is an independent system software service program. Distributed application software uses this software to share resources between different technologies. The middleware is located on the operating system of the client server. Manage computing resources and network communications.

First of all, middleware is a general term for a certain type of software, not a specific software . It is a platform (operating system hardware) and application program . It shields the complexity of the underlying operating system and reduces the technical burden of developers. At the same time, its design is not specific to a certain The specific goal is to provide functional module services with universal features. These services have standard program interfaces and protocols, and can be implemented differently depending on the platform.

Popular examples (for reference only, not exactly the same):

  • I opened a coffee shop. I have n coffee beans suppliers such as ABC and so on. But I must choose beans with affordable prices and good quality. However, the market is fluctuated by many factors. Maybe my current choice , After a period of time is not the best option anymore. So I specifically found a market intermediary and asked him to help me worry about this stall. I only tell you about the price and quality requirements, and you can find it. I don't worry about the process at all. The concept of this intermediary is similar to that of middleware

1.1.1 Distributed concept (supplement)

This paragraph comes from the article I wrote earlier about Dubbo Introduction Ha

are relatively professional and obscure. Most blogs or tutorials often use the definition in "Principles and Paradigms of Distributed Systems", namely: 160c833e35636a "A distributed system is a collection of several independent computers. These computers For the user it is like a single related system"

Below we use some space to explain in general what is called distributed

1.1.1.1 What is a centralized system

When it comes to distributed, I have to mention "centralized system" , this concept is best understood, it is to install functions, programs, etc. on the same device, and this host device provides services to the outside.

To give the simplest example: You take a PC host and modify it into a simple server. After configuring various contents, you install MySQL, Web server, FTP, Nginx, etc., all in it, After packaging and deploying the project, you can provide services to the outside world, but once this machine has problems with either software or hardware, the entire system will be seriously implicated. The eggs are placed in a basket and they are all beaten if they want to beat them.

1.1.12 What is a distributed system

Since the centralized system has such a problem that affects the whole body, one of the functions of the distributed system is naturally to solve such problems. As known in the definition, the distributed system is in the user's sense of experience. Just like a traditional single system, some changes are made internally by the system itself, and there is not much feeling for users.

For example: Taobao, JD.com and other large e-commerce platforms have tens of thousands of hosts. Otherwise, they will not be able to handle a large amount of data and requests. What are the specific divisions and operations, we will talk about below, but For us as users, we don’t need and don’t want to care about these. We can still simply think that what we are facing is the "host" of "Taobao".

Therefore, a relatively professional statement of distributed is this (process granularity) Two or more programs, running on different host processes, cooperate with each other to complete common functions, then one of these programs The system constituted in time can be called a distributed system

  • These are all the same program-distributed
  • These are all different programs-clusters

1.2 What is message middleware/message queue (MQ)

Message middleware, as the name implies, is a middleware used to process message-related services. It provides a communication and interaction channel between systems. For example, the sender only needs to transfer the information that it wants to transmit to the message middleware, and the sending protocol, Modes, network, faults and other issues during the sending process are all handled by the middleware, so it is responsible for ensuring the reliable transmission of information.

Therefore, message middleware is a technology used to receive data, store data, and send data. It provides various functions, can achieve high availability and reliability of messages, and also provides a good fault tolerance mechanism. The program can greatly help the occupation of system resources and the improvement of transmission efficiency.

  • MQ often refers to the message queue, that is, Message Quene. Common message queues include classic ActivieMQ, popular Kafka, Ali's RocketMQ, etc., as well as RabbitMQ explained here.

    • Different MQs have different characteristics and the direction they are better at. It is hard to say who is good and who is bad, only who is more suitable.

1.2.1 Application Scenarios of Message Queue

According to the needs of the business, it can actually have a variety of application scenarios, such as decoupling, peak-shaving and valley-filling, broadcasting, etc. Let’s take two scenarios to sort out the simple process

1.2.1.1 Business decoupling

I’m considering buying a few books recently, and I’ll take an order for buying a book as an example. When I click to buy, there may be such a series of business logic execution, ① subtract the inventory capacity ② generate an order ③ pay ④ update the order status ⑤ send the purchase successfully SMS ⑥ Update the delivery status of the goods. In the initial stage, we can fully execute these services simultaneously, but in the later stage, in order to improve efficiency, we can separate the tasks that need to be executed immediately and the tasks that can be executed later, such as ⑤ sending a successful purchase SMS ⑥ updating the status of the goods courier collection , You can consider different execution. After the execution of the main process ends, these services that can be slowed down can be determined to have been executed by sending a message to the MQ to ensure that the process ends first. Then pull MQ messages, or MQ proactively push to perform other services asynchronously.

1.2.1.2 Peak shaving and valley filling

For example, sending an announcement message with a read and unread flag, so you need to write such an announcement message to each user, for example, save it in MongoDB, even MongoDB cannot support instantaneous writing of millions or tens of millions of records Circumstances, so you can consider using message queues. For example, on the Java back-end system, we can use asynchronous multithreading to send messages to the message queue MQ, so that the Web system does not occupy the normal CRUD operations of the database when publishing announcement messages. System messages are stored in the message queue. We just use it to cut peaks and fill valleys. The system messages will eventually be stored in the database. So we can design this way, when a user logs in to the system, use an asynchronous thread to receive the user's system message from the message queue MQ, and then store the system message in the database, and finally the message in the message queue MQ is automatically deleted. Because of the user's staggered login, the task of writing messages in the database in the past has also become staggered writing.

1.3 What is RabbitMQ

RabbitMQ is an open source message queuing system written in Erlang language and following the AMQP protocol. It supports multiple clients (languages) for storing and forwarding messages in a distributed system. It has high availability, high scalability, and easy Features such as usability.

For a more detailed introduction, you can directly look at the official website:

In short, this is a common message queue. These features will be explained one by one later. We first start with the download and installation part of the entry, and then use it.

2. Download and install

Generally speaking, the installation methods include manual installation and Docker installation. In most scenarios, Docker installation is used. However, as a learning stage, if you are not particularly anxious, it is not a bad thing to learn manual installation.

Note: Both cloud servers and virtual machines are available, and the Linux version demonstrated is CentOS 7.9

2.1 Manual installation

2.1.1 Download and installation process

Note: You can download and install directly through yum in Linux. Here, I chose to download the file on my own Windows host first, and then upload it to Linux via FTP for direct installation. Some download problems caused by the network on the virtual machine can be avoided.

  1. First open the download directory of the official website, and then select the version according to your Linux version.

  1. Because RabbitMQ is written in Erlang language, you also need to provide an Erlang environment, and then download Erlang.

  1. Upload the file to Linux (I specify the location here is /usr/local/bin/rabbitmq, you can change the selection yourself)

    • Many Shell software now comes with built-in FTP upload, such as FinalShell, MobaXterm, etc.
    • The uploaded files and directory locations are as follows
[root@centos7 rabbitmq]# ls
esl-erlang_23.2.3-1_centos_7_amd64.rpm  rabbitmq-server-3.8.14-1.el7.noarch.rpm
[root@centos7 rabbitmq]# pwd
/usr/local/bin/rabbitmq
  1. Install Erlang, Socat and RabbitMQ

    • Erlang and Socat are all dependent on RabbitMQ
# 安装 Erlang,安装后执行 erl -v 显示版本号则代表成功
rpm -ivh esl-erlang_23.2.3-1_centos_7_amd64.rpm

# 安装 Socat 这里没有下载源文件,而是直接通过 yum 在线安装,因为它并不大
yum install -y socat

# 安装 RabbitMQ
rpm -ivh rabbitmq-server-3.8.14-1.el7.noarch.rpm
  1. The installation is over, start the service to check whether RabbitMQ can be started successfully
# 启动服务
systemctl start rabbitmq-server
# 开机自启
systemctl enable rabbitmq-server
# 停止服务
systemctl stop rabbitmq-server
# 查看服务状态
systemctl status rabbitmq-server.service

As shown in the figure, the installation starts successfully

If the installation is wrong, please refer to:

2.1.2 Configure Web interface management

The above installation is actually over, but RabbitMQ provides us with a web management interface, which is not available by default and needs to be installed.

  1. Install the web management plug-in, then restart the service
# 安装命令
rabbitmq-plugins enable rabbitmq_management

# 重启服务
systemctl restart rabbitmq-server
  1. Be sure to open the 15672 port of the Linux firewall, otherwise you will not be able to access it. During the learning phase, you can even go to the query command to turn off the firewall

    • The corresponding server (Ali Cloud, Tencent Cloud, etc.) is to open port 15672 in the security group
    • Access Linux IP: 15672, such as http://192.168.122.1:15672
# 查询 15672 是否开放,一般默认都是 no
firewall-cmd --query-port=15672/tcp
# 开放指定端口 15672 
firewall-cmd --add-port=15672/tcp --permanent
# 重新载入
firewall-cmd --reload
# 再次查询,结果就是 yes 了
firewall-cmd --query-port=15672/tcp
  1. Add a remote login account

    • RabbitMQ has a default account and password that are both guest but can only be accessed under localhost
# 新增用户 用户名和密码都是 admin
rabbitmqctl add_user admin admin
  1. Add permissions for remote login accounts

    • Administrator (super administrator): log in to the console, view all information, operate users, and operation strategies
    • monitoring (monitor): Log in to the console and view all information
    • policymaker (policy maker): log in to the console, specify the policy
    • managment (ordinary administrator): Log in to the console
# 设置用户分配操作权限,admin 用户的权限为 administrator
rabbitmqctl set_user_tags admin administrator
  1. Add resource permissions for users

    • Because admin is already a super administrator, it does not need to be assigned resource permissions. It will do so by default.
# 命令格式为: set_permissions [-p <vhostpath>] <user> <conf> <write> <read>
# 这里即为 admin 用户开启 配置文件和读写的权限
rabbitmqctl set_permissions -p / admin ".*"".*"".*"
  1. Access Linux IP: 15672, such as http://192.168.122.1:15672 , enter the username and password admin that you just set

    • As shown in the figure: successful access

2.1.2.1 Command summary
  1. Add user: rabbitmqctl add_user <username> <password>
  2. Change password: rabbitmqctl change_password <username> <newpass>
  3. Delete user: rabbitmqctl delete_user <username>
  4. User list: rabbitmqctl list_users
  5. Set user role: rabbitmqctl set_user_tags <username> <tag1,tag2>
  6. Delete all roles of the user: rabbitmqctl set_user_tags <username>
  7. Add resource permissions for the user: set_permissions [-p <vhostpath>] <user> <conf> <write> <read>

Use: Enter rabbitmqctl, you will be prompted for possible commands, and then use rabbitmqctl hepl <command> to view the usage and parameters of specific commands.

2.1.3 Brief introduction to Web interface management

  • Connections: used here to manage producers and consumers after establishing a connection with RabbitMQ
  • Channels: After the connection is established, a channel will be formed, and the delivery of messages depends on the channel.
  • Exchanges: used to implement message routing
  • Queues (queues): queues for storing messages, messages waiting to be consumed, and removed from the queue after consumption.
  • Admin (management): used to set the management user and the corresponding authority, as shown in the figure below

Tags are used to specify the role of the user

  • Administrator (super administrator): log in to the console, view all information, operate users, and operation strategies
  • monitoring (monitor): Log in to the console and view all information
  • policymaker (policy maker): log in to the console, specify the policy
  • managment (ordinary administrator): Log in to the console

2.2 Docker installation

Installing RabbitMQ in Docker does not need to consider various conflicts and incompatibility issues such as version and environment. It is very convenient. The virtual machine I demonstrated is a CentOS 7.9 bare metal, so we went from updating yum to installing Docker and Install RabbitMQ and follow the steps

2.2.1 Configure yum

  1. Update yum to the latest version
# 更新 yum
yum update

# 检查yum依赖的几个包 yum-utils 提供 yum-config-manager 功能, 后面两个是 devicemapper 用到的
yum install -y yum-utils device-mapper-persistent-data lvm2
  1. Set yum source to Alibaba Cloud
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

2.2.2 Install docker

2.2.2.1 Procedure
  1. Use yum to install docker

    • docker-ce means community edition, ee means enterprise edition
yum install docker-ce -y
  1. Check if the installation is successful by checking the version
docker -v
  1. Docker image acceleration (here <your ID> needs to be replaced with your own)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://<你的ID>.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
2.2.2.2 Docker common commands

2.2.2.2.1 Management commands

  • Just start, stop, restart these simple commands is also possible to use service, systemctl is slightly more powerful
# 启动 docker
systemctl docker start
# 停止 docker
systemctl docker stop
# 重启 docker
systemctl docker restart
# 查看 docker 状态
systemctl status docker
# 开机自启
systemctl enable docker
systemctl unenable docker

2.2.2.2.2 Mirror command

# 导入镜像文件
docker load < xxx.tar.gz
# 查看安装的镜像
docker images
# 删除镜像
docker rmi 镜像名

2.2.3 Install RabbitMQ (choose one)

Note: It would be better to use 2.2.3.2 directly to install in one sentence

2.2.3.1 Step by step installation
  1. Get the mirror of RabbitMQ
docker pull rabbitmq:management
  1. Create and run the container (specific parameters are introduced in 3)
docker run -id --name 容器名 -p 15672:15672 -p 5672:5672 rabbitmq:management
2.2.3.2 One-sentence installation

The above installation method is to obtain the RabbitMQ image first and then start the installation. There is no problem here. There will be a problem when creating it, because we need to install management, which is its web management. If you don’t do some processing, it will be installed by default. There is no user, so you need to configure it yourself as before. Docker Hub has already given an example of our configuration, that is, use -e represent the configuration, and use RABBITMQ_DEFAULT_USER and RABBITMQ_DEFAULT_PASS configure the user name and password.

For more information, please see the section Setting default user and password in the official example given by Docker Hub

https://registry.hub.docker.com/_/rabbitmq/

  1. Perform installation
docker run -di --name myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
  1. Check whether the operation is successful through the container status
# 查看容器运行状态
docker ps -a
# 启动
docker start 容器名
# 停止
docker stop 容器名
# 退出命令行,不停止
exit
# 进入到node容器(如果开启了 -t 的情况)
docker exec -it 容器名 bash

Parameter introduction

The following explains the description of these parameters:

  • -i : Represents the running container.
  • -t : Indicates that the interactive mode (command line) is reserved for the container, that is, a pseudo terminal is allocated. So often see -it such a match.
  • --name : Give the container a name.
  • -v : Represents the directory mapping relationship (the former is the host directory, the latter is the directory mapped to the host), multiple -v can be used for multiple directory or file mapping. Note: It is recommended to do directory mapping, make changes on the host, and then share to the container.
  • -d : Indicates that a guardian container is created to run in the background (so that the container will not be automatically logged in after the container is created. If you only add the -i -t , it will automatically enter the container after creation), that is, the backend will be suspended.
  • -p : Represents port mapping. The former is the host port and the latter is the mapped port in the container. You can use multiple -p do multiple port mapping. Only after port mapping can it be accessed by the outside world.

Let me give you an example:

# 创建容器,把容器 3000 端口映射到宿主机 3000 端口,把/demo映射到宿主机的/demo  face是我下载好的一个现成的镜像
docker run -d -it -p 3000:3000 -v /demo:/demo --name node face

# 例如,名为 node 的镜像中有一个需要执行的 python 程序,就可以通过如下命令进入刚才分配到的命令行中去执行这个程序
docker exec -it node bash
  • Because -t used, it can be assigned to a pseudo terminal and enter the command line docker exec -it container name bash
  • -v directory is mapped, after entering the container, there will also be an identical demo folder, for example, python programs can be executed in it

Port introduction

4369: Erlang discovers the port

5672: client communication port

15672: Management interface ui port

25672: Internal communication port between servers

61613: STOMP client without TLS and with TLS

1883: MQTT client without and with TLS enabled

The more important ones are 5672 and 15672

For more port details, please visit the official website document

Note: If you want to connect remotely, such as accessing port 15672 of the web management page, port 5672 of the Java client connection, an open operation must be performed, otherwise the connection will not be possible.

  • The following is an example of opening port 15672 based on CentOS 7.9
# 查询 15672 是否开放,一般默认都是 no
firewall-cmd --query-port=15672/tcp
# 开放指定端口 15672 
firewall-cmd --add-port=15672/tcp --permanent
# 重新载入
firewall-cmd --reload
# 再次查询,结果就是 yes 了
firewall-cmd --query-port=15672/tcp
  • The following is the command to turn off the firewall
systemctl disable firewalld
systemctl stop firewalld   

3. RabbitMQ protocol and model

After the installation is over, it is necessary to enter the topic, that is, several ways to implement RabbitMQ with Java or Springboot code, but if you want a good understanding of these routing exchange methods, you need to have an understanding of its protocol and architecture model.

3.1 Agreement

3.1.1 What is an agreement?

Protocol, short for network protocol, network protocol is a set of agreements that must be complied with by both parties of the communication computer. Such as how to establish a connection, how to identify each other, etc. Only by observing this agreement can computers communicate with each other. Its three elements are: grammar, semantics, and timing.

In order for data to travel from the source to the destination on the network, the participants in network communication must follow the same rules. This set of rules is called a protocol, which is ultimately reflected in the format of data packets transmitted on the network.

3.1.1.1 The three elements of network protocol
  1. Syntax: The structure and format of data and control information, and the order in which data appears.
  2. Semantics: Explain the meaning of each part of the control information, and specify what kind of control information needs to be sent and what response to the completed action.
  3. Timing: A detailed description of the sequence of events.

People vividly describe these three elements as: what to do, how to do it, and the order of doing it.

For example, HTTP protocol

Syntax: HTTP specifies the format of request message and response message
Semantics: The client actively initiates a request, which is called a request, and the server then returns data, which is called a response
Timing: A request corresponds to a response, and there is a response after the request

3.1.1.1.1 Interview question: Why doesn’t the message middleware directly use the HTTP protocol

For a message middleware, its main responsibility is to be responsible for data transfer, storage, and distribution. High performance and simplicity are what we pursue, while HTTP request headers and response headers are more complicated, including cookies. , Data encryption and decryption, window sill, response code and other additional functions, we don’t need such complicated functions.

At the same time, in most cases, HTTP is mostly short links. In the actual interaction process, a request to a response is likely to be interrupted. After the interruption, persistence will not be performed, which will cause the loss of the request. This is not conducive to the business scenario of message middleware, because message middleware may be a long-term process of obtaining information. When problems and failures occur, data or messages must be persisted. The purpose is to ensure the high reliability and reliability of messages and data. Robust operation

3.1.2 AMQP protocol of RabbitMQ

The protocol used by RabbitMQ is AMQP (advanced message queuing protocol), which was proposed in 2003 and was first used to solve the problem of message passing and interaction between different platforms in the financial sector.

AMQP is more accurately a binary wire-level protocol (link protocol). This is the essential difference between it and JMS. AMQP does not define the API layer, but directly defines the data format exchanged by the network. This makes the Provider (Producer) that implements AMQP naturally cross-platform.

Compared with other message protocols, its characteristics are:

  1. Distributed transaction support
  2. Message persistence support
  3. High performance and high reliability message processing advantages

3.1.3 Architecture model

If you want to learn the specific sending modes of the next few messages, this model diagram must be understood clearly, because these methods are different degrees of selection and reduction of this model.

  • Producer : The producer of the message (the program that sends the message).
  • Connection : The network connection between the application and the Broker.
  • Channel : Channel, that is, the channel for information transmission, multiple Channels can be established, and each Channel represents a session task.

    • A channel is a virtual connection established in a TCP connection. The reading and writing of information is transmitted through the channel. Because it is very expensive for the operating system to establish and destroy TCP, the concept of a channel is introduced to reuse a TCP connection.
  • Broker(Server) : Identifies the message queue server entity, for example, RabbitMQ Server here.
  • Virtual Host : Virtual host, multiple Virtual Hosts can be set up in a Broker, which can be used to isolate the permissions of different users.

    • Broker can be understood as the entire database service, and Virtual Host is the feeling of each database. Different projects can correspond to different databases, including the business table to which the project belongs, and so on.
    • In each Virtual Host, there can be several Exchanges and Queues.
  • Exchange : The switch is used to receive the messages sent by the producer, and then send these messages to the queue according to the routing key.
  • Binding : Virtual connection between Exchange and Queue. Multiple Routing keys can be included in Binding.
  • Routing key : Routing rule, which is used by the virtual machine to confirm how to route a specific message.
  • Queue : Message queue, which is a container for messages, used to store messages. Each message can be transferred to one or more queues, waiting for consumers to consume, that is, take out the message.
  • Consumer : The consumer of the message (the program that receives the message).

4. Java implements RabbitMQ

4.1 Environment setup

The official website introduces several models: https://www.rabbitmq.com/getstarted.html

So far, the official website has provided a total of 7 models. We mainly introduce the first five basic modes. Some people also classify both Direct and Topic modes as Routing mode, which can also be regarded as four types.

4.1.1 Create Java Project

First create a Maven project that does not use a skeleton, and then introduce RabbitMQ dependencies and unit test dependencies.

<dependency>
   <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.10.0</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
</dependency>

4.1.2 Create a virtual host (optional)

Here, we created a new Virtual Hosts to serve this Java project. You can also create a new user, and then enable access to this Virtual Hosts (ie, bind the virtual host to the user). Here we still use admin (a user with administrator privileges I created before) to demonstrate.

Note: This part can be omitted, and it can be used directly with / and the admin user

4.1.3 Create a connection tool class

Since we are going to demonstrate a variety of examples later, and every time the operation code of obtaining connection, releasing connection, closing resources, etc. is the same, in order to prevent code redundancy, optimize the code, and make it easier to understand, extract a tool class, so that everyone will focus Just put it on the comparison of different implementations.

  • RabbitMqUtil tool class
public class RabbitMqUtil {
    /**
     * 主机名 即 Linux IP地址
     */
    private static String host = "";
    /**
     *  端口号 客户端访问默认都是 5672
     */
    private static int port = 0;
    /**
     * 虚拟主机 可以设置为默认的 / 或者自己创建出指定的虚拟主机
     */
    private static String virtualHost = "";
    /**
     * 用户名
     */
    private static String username = "";
    /**
     * 密码
     */
    private static String password = "";

    // 使用静态代码块为Properties对象赋值
    static {
        try {
            //实例化对象
            Properties properties = new Properties();
            //获取properties文件的流对象
            InputStream in = RabbitMqUtil.class.getClassLoader().getResourceAsStream("rabbitmq.properties");
            properties.load(in);
            // 分别获取 value
            host = properties.getProperty("host");
            port = Integer.parseInt(properties.getProperty("port"));
            virtualHost = properties.getProperty("virtualHost");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     *
     * @return 连接
     */
    public static Connection getConnection() {
        try {
            // 创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            // 设置连接 rabbitmq 主机
            connectionFactory.setHost(host);
            // 设置端口号
            connectionFactory.setPort(port);
            // 设置连接的虚拟主机(数据库的感觉)
            connectionFactory.setVirtualHost(virtualHost);
            // 设置访问虚拟主机的用户名和密码
            connectionFactory.setUsername(username);
            connectionFactory.setPassword(password);
            // 返回一个新连接
            return connectionFactory.newConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭通道和释放连接
     *
     * @param channel    channel
     * @param connection connection
     */
    public static void close(Channel channel, Connection connection) {
        try {
            if (channel != null) {
                channel.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • properties
host=192.168.122.1
port=5672
virtualHost=/rabbitmq_maven_01
username=admin
password=admin

4.2 Five ways to achieve

Description:

  • String content such as queue name, message, etc., is more recommended to be defined as a variable to pass in. In my text, I write it directly in the parameter. This kind of magic value is not very elegant.
  • The producer uses the Junit unit test, but the consumer is written in the main function. This is because we want the consumer to be in a state of continuous running and waiting. If Junit is used, the program will end after being executed once.

    • In addition to writing in the main function, you can also consider using sleep or while(true) to prevent the program from terminating directly.

4.2.1 Simple Queue Mode (Hello Word)

  • Producer : The producer of the message (the program that sends the message).
  • Queue : Message queue, understood as a container, the producer sends a message to it, it stores the message, and waits for the consumer to consume it.
  • Consumer : The consumer of the message (the program that receives the message).
4.2.1.1 How to understand

As shown in the figure, in the simple queue mode, a producer passes through a queue and corresponds to a consumer. It can be regarded as a point-to-point transmission method. Compared with the model diagram in 3.1.3, the main feature is that Exchange (switch) and routekey (route key) cannot be seen. It is precisely because this mode is simple, so It does not involve complicated conditional distribution, etc., so there is no need for users to explicitly consider the issue of switches and routing keys.

  • But note that this mode is not that the producer directly docks the queue, but uses the default switch. The default switch will send the message to the queue with the same name as the routekey. This is also the position of the routekey in the code that follows. The reason for filling in the queue name
4.2.1.2 Code implementation

4.2.1.2.1 Producer code

public class Producer {
    @Test
    public void sendMessage() throws IOException, TimeoutException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        Channel channel = connection.createChannel();
        // 通道绑定消息队列
        channel.queueDeclare("queue1",false,false,false,null);
        // 发布消息
        channel.basicPublish("","queue1",null,"This is rabbitmq message 001 !".getBytes());
        // 通过工具关闭channel和释放连接
        RabbitMqUtil.close(channel,connection);
    }
}
  1. Get connections through tools
  2. Get the connection channel : According to the model diagram in 3.1.3, the producer needs to get the connection and then get the channel to access the subsequent switch queues and so on.
  3. channel binding message queue : Before binding the queue, the switch should be bound, but the concept of the switch is hidden in this mode, and the default switch is used behind it, so the queue is bound directly.

    • QueueDeclare method explanation

      • Parameter 1: queue (queue name), if the queue does not exist, it will be created automatically.
      • Parameter 2: durable (whether the queue is persistent), persistence can ensure that the queue still exists after the server restarts.
      • Parameter 3: exclusive (exclusive queue) means whether to exclusive the queue, if this item is true, the queue is only visible to the connection that declares it for the first time, and is automatically deleted when the connection is disconnected.
      • Parameter 4: autoDelete (automatic deletion), after the last consumer consumes the message, the queue is automatically deleted.
      • Parameter 5: arguments (carry additional attributes).
  4. publish message : Here you can specify the sending method and content of the message queue, because this mode is relatively simple, so all parameters are not involved, and the following mode will be explained in detail

    • BasicPublish method explanation

      • Parameter 1: exchange (switch name).
      • Parameter 2: routingKey (routing key), fill in the queue name here, which can be understood as sending the message to the queue with the same name as the routekey.
      • Parameter 3: props (the control state of the message), where you can control the persistence of the message.

        • The parameter is: MessageProperties.PERSISTENT_TEXT_PLAIN
      • Parameter 4: body (message body), the type is a byte array, you need to change the type.
  5. close the channel and release the connection through the tool : first close the channel, and then release the connection.

4.2.1.2.2 Consumer code

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException{
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        Channel channel = connection.createChannel();
        // 通道绑定消息队列
        channel.queueDeclare("queue1", false, false, false, null);
        // 消费消息
        channel.basicConsume("queue1", true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("new String(body): " + new String(body));
            }
        });
    }
}
  1. Get connection through tools
  2. Get the connection channel
  3. Channel binding message queue
  4. consumption message : here is used to specify which queue message to consume, as well as some mechanisms and callbacks

    • BasicConsume method explanation

      • Parameter 1: queue (queue name), that is, which queue message is consumed.
      • Parameter 2: autoAck (automatic answer) starts the automatic confirmation mechanism of the message, and deletes the message from the queue as long as it is consumed.
      • Parameter 3: callback (callback interface during consumption), the type of callback is Consumer, where DefaultConsumer is used, which is an implementation class of Consumer. Rewrite the handleDelivery method to get the content of the consumed data. The body is mainly used here, that is, to view the message body. The other three parameters are not used yet. If you are interested, you can print it out first. A general understanding.

4.2.2 Work Queue Mode (Work Queue)

  • Producer : The producer of the message (the program that sends the message).
  • Queue : Message queue, understood as a container, the producer sends a message to it, it stores the message, and waits for the consumer to consume it.
  • Consumer : The consumer of the message (the program that receives the message).

    • Here we assume that Consumer1, Consumer2, and Consumer3 are consumers who complete tasks at different speeds, respectively, which will lead to a key issue of this model.
4.2.2.1 How to understand

The working mode can be seen from the figure, that is, on the basis of the simple queue mode, multiple consumers are added, that is, multiple consumers are bound to the same queue and consume together, which can solve the simple queue mode. The production speed is much faster than the consumption speed, which results in the accumulation of news.

  • Because the message will disappear after being consumed, there is no need to worry about the task being executed repeatedly.
4.2.2.2 Code Implementation

Note: There are two work queue modes

  1. Polling mode: each consumer divides the message equally
  2. Fair distribution mode (those who can do more work): Distribute according to ability, distribute more with fast processing speed, and distribute less with slow processing speed

The first thing we demonstrate is the polling mode. According to its shortcomings, it can lead to a fair distribution mode.

The following describes only the differences from the above. In the simple mode, these basic methods have been introduced.

4.2.2.2.1 Polling Mode-Producer Code

public class Producer {
    @Test
    public void sendMessage() throws IOException, TimeoutException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        Channel channel = connection.createChannel();
        // 通道绑定消息队列
        channel.queueDeclare("work", true, false, false, null);
        for (int i = 1; i <= 20; i++) {
            // 发布消息
            channel.basicPublish("", "work", null, (i + "号消息").getBytes());
        }
        // 通过工具关闭channel和释放连接
        RabbitMqUtil.close(channel, connection);
    }
}

The process is basically the same as the simple queue mode. There are some minor changes. The producer mainly adds a layer of loops. Because there are multiple consumers, more messages are sent, and some characteristics and problems can be seen.

4.2.2.2.2 Polling Mode-Consumer Code

  • Consumer 1
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        final Channel channel = connection.createChannel();
        // 通道绑定消息队列
        channel.queueDeclare("work", true, false, false, null);
        // 消费消息
        channel.basicConsume("work", true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者1号:消费-" + new String(body));
            }
        });
    }
}
  • Consumer 2
public class Consumer2 {
    public static void main(String[] args) throws IOException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        final Channel channel = connection.createChannel();
        // 通道绑定消息队列
        channel.queueDeclare("work", true, false, false, null);
        // 消费消息
        channel.basicConsume("work", true, 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));
            }
        });
    }

The above two consumers have enabled automatic Ack response in basicConsume, which will be detailed below. At the same time, in consumer 1, a statement of sleep 2s has been added to simulate that consumer 1 is slow in processing messages, while consumer 2 is processing A scene where the message speed is fast.

operation result:

  • Consumer1
消费者1号:消费-1号消息
消费者1号:消费-3号消息
消费者1号:消费-5号消息
消费者1号:消费-7号消息
消费者1号:消费-9号消息
消费者1号:消费-11号消息
消费者1号:消费-13号消息
消费者1号:消费-15号消息
消费者1号:消费-17号消息
消费者1号:消费-19号消息
  • Consumer2
消费者2号:消费-2号消息
消费者2号:消费-4号消息
消费者2号:消费-6号消息
消费者2号:消费-8号消息
消费者2号:消费-10号消息
消费者2号:消费-12号消息
消费者2号:消费-14号消息
消费者2号:消费-16号消息
消费者2号:消费-18号消息
消费者2号:消费-20号消息

Observe the execution process: It is found that although each of the two consumers processed half of the messages in the end, and was distributed according to one person, the processing speed of consumer 2 was fast, and all of them were processed at once, but consumer 1 , Each processing takes 2s, so it can only be processed slowly, and consumer 2 is in a situation of idle waste.

to fair distribution mode?

This is related to the second parameter in basicConsume, enabling automatic confirmation of consumption. It is true by default, which means that as long as I get the message distributed to this consumer in the queue, I will automatically return a confirmation of consumption. , The message in the queue will be deleted automatically after the queue is received.

  • But there is a very important problem in this way. This approach is to pass the risk to the consumer. For example, the consumer has received 10 messages that he needs to process, just consumed 4, and the consumer is down and hangs up. The next 6 messages are lost.

If you want to modify it to a method of capacity allocation, there are two main points

  1. Set the channel to consume only one message at a time
  2. Turn off automatic confirmation of messages, and manually confirm messages

4.2.2.2.3 Fair Distribution Mode-Producer Code

public class Producer {
    @Test
    public void sendMessage() throws IOException, TimeoutException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        Channel channel = connection.createChannel();
        // 一次只发送一条消息
        channel.basicQos(1);
        // 通道绑定消息队列
        channel.queueDeclare("work", true, false, false, null);
        for (int i = 1; i <= 20; i++) {
            // 发布消息
            channel.basicPublish("", "work", null, (i + "号消息").getBytes());
        }
        // 通过工具关闭channel和释放连接
        RabbitMqUtil.close(channel, connection);
    }

4.2.2.2.4 Fair Distribution Mode-Consumer Code

  • Consumer 1
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        final Channel channel = connection.createChannel();
        // 一次只接受一条未确认的消息
        channel.basicQos(1);
        // 通道绑定消息队列
        channel.queueDeclare("work", true, false, false, null);
        // 消费消息
        channel.basicConsume("work", false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者1号:消费-" + new String(body));
                // 返回 deliveryTag 代表队列可以删除此消息了
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}
  • Consumer 2
public class Consumer2 {
    public static void main(String[] args) throws IOException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        final Channel channel = connection.createChannel();
        //步骤一:一次只接受一条未确认的消息
        channel.basicQos(1);
        // 通道绑定消息队列
        channel.queueDeclare("work", true, false, false, null);
        // 消费消息
        channel.basicConsume("work", false, 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));
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }

operation result:

  • Consumer1
消费者1号:消费-1号消息
  • Consumer2
消费者2号:消费-2号消息
消费者2号:消费-3号消息
消费者2号:消费-4号消息
消费者2号:消费-5号消息
消费者2号:消费-6号消息
消费者2号:消费-7号消息
消费者2号:消费-8号消息
消费者2号:消费-9号消息
消费者2号:消费-10号消息
消费者2号:消费-11号消息
消费者2号:消费-12号消息
消费者2号:消费-13号消息
消费者2号:消费-14号消息
消费者2号:消费-15号消息
消费者2号:消费-16号消息
消费者2号:消费-17号消息
消费者2号:消费-18号消息
消费者2号:消费-19号消息
消费者2号:消费-20号消息

4.2.3 Publish and Subscribe Mode (Fanout Broadcast)

  • Producer : The producer of the message (the program that sends the message).
  • Exchange : The switch is responsible for sending messages to the designated queue.
  • Queue : Message queue, understood as a container, the producer sends a message to it, it stores the message, and waits for the consumer to consume it.
  • Consumer : The consumer of the message (the program that receives the message).
4.2.3.1 How to understand

Fanout is literally translated as "fan-out", but everyone will call it broadcast or publish and subscribe. It is a mode without routing key. The producer sends the message to the exchange, and the exchange will copy and synchronize all the messages to all and It is bound to the queue, and each queue can only have one consumer to get this message. If multiple channels are created in a consumer connection, there will be a result of contention for the message.

4.2.3.2 Code Implementation

Note: The following describes only the differences from the above. In the simple mode, these basic methods have been introduced

4.2.3.2.1 Producer code

public class Producer {
    @Test
    public void sendMessage() throws IOException, TimeoutException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        // 获取连接通道
        final Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare("order", "fanout");
        for (int i = 1; i <= 20; i++) {
            // 发布消息
            channel.basicPublish("order", "", null, "fanout!".getBytes());
        }
        // 通过工具关闭channel和释放连接
        RabbitMqUtil.close(channel, connection);
    }
}
  1. declare switch

    • exchangeDeclare method explanation

      • Parameter 1: exchange (switch name), if the switch does not exist, it will be created automatically
      • Parameter 2: type, choose fanout mode here
  2. publish message : Enter the name of the above-defined switch in the first parameter of the basicPublish method, and the second parameter, the routing key is empty

    • 20 loops are to demonstrate consumers

4.2.3.2.2 Consumer code

  • Consumer 1
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明交换机
        channel.exchangeDeclare("order", "fanout");
        // 创建临时队列
        String queue = channel.queueDeclare().getQueue();
        // 绑定临时队列和交换机
        channel.queueBind(queue, "order", "");
        // 消费消息
        channel.basicConsume(queue, true, 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));
            }
        });
    }
}
  1. declare switch
  2. Create temporary queue
  3. bind temporary queue and switch

    • QueueBind method explanation

      • Parameter 1: queue (temporary queue)
      • Parameter 2: exchange
      • Parameter 3: routingKey (routing key)
  • Consumer 2: Demonstrated the situation of multiple channels in a connection
public class Consumer2 {
    public static void main(String[] args) throws IOException {
       // 通过工具类获取连接
        Connection connection = RabbitMqUtil.getConnection();
        
        // 获取连接通道
        Channel channel = connection.createChannel();
        Channel channel2 = connection.createChannel();
        
        // 声明交换机
        channel.exchangeDeclare("order", "fanout");
        channel2.exchangeDeclare("order", "fanout");
        
        // 创建临时队列
        String queue = channel.queueDeclare().getQueue();
        System.out.println(queue);
        
        // 绑定临时队列和交换机
        channel.queueBind(queue, "order", "");
        channel2.queueBind(queue, "order", "");
        
        // 消费消息
        channel.basicConsume(queue, true, 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));
            }
        });
        
        // 消费消息
        channel2.basicConsume(queue, true, new DefaultConsumer(channel2) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者2-2号:消费-" + new String(body));
            }
        });
    }
}

operation result:

消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!
消费者2-2号:消费-fanout!

4.2.3.2.3 Why do consumers also declare switches?

As can be seen from the above code, in Producer and Conusmer, we have declared the switch separately, but consumers can know from the figure that they will not have direct contact with the switch. Why do consumers also declare the switch?

This is to ensure that when the Producer or Producer is executed, there will never be errors because the switch has not been declared. For example, if you only declare the switch in the Producer, then you must start the Producer first. If you execute the Conusmer dir


二境志
191 声望26 粉丝