Detailed explanation of RabbitMQ and working mode, including docker deployment

Note: This article sorts out some of the knowledge points of some excellent rabbitmq blogs on the Internet, and I will not list them here. It is mainly to sort out the teaching process of the bad programming teacher at station b (very good teacher, very friendly to novices, A wave of Amway to everyone)

RabbitMQ

The detailed tutorial will take you to learn RabbitMQ. We learn every technology with questions. Why should we learn RabbitMQ? What is RabbitMQ? RabbitMQ? And finally how we use the code to operate RabbitMQ;

What is RabbtiMQ?

  • RabbitMQ is a message middleware based on the AMQP protocol. It introduces the concept of a switch based on the basic message queue (the producer sends the message to the message middleware, and the message middleware sends the message to the consumer). The producer can choose to send the message It is sent to the switch, and the switch sends it to the consumer according to the specified routing rules .

What are the usage scenarios of message middleware (RabbitMQ)?

  • asynchronous processing
  • System decoupling
  • flow clipping
  • newsletter

What is AMQP

AMQP (Advanced Message Queuing Protocol) is a protocol standard for application layer transmission. It was born for message middleware. Based on this protocol, the client and the message center can support communication between different products and different languages.
AMQP contains some important core members:

  • The ConnectionFacotry connection factory is responsible for creating connections
  • Connection connection is responsible for network connection with message middleware
  • channel The channel is a lightweight connection built on the Connection, where all read and write operations on the message queue are performed.
  • broker middleware Middleware that stores messages. rabbitmq is.
  • The virtualHost virtual machine is responsible for providing an isolated production environment, with no association between multiple virtual hosts.
  • The exchange switch sends the received message to the consumer according to the set routing rules

Rabbitmq common usage

Rabbitmq includes several commonly used routing methods, which connection method to use is determined according to different usage scenarios
(the code part will be written one by one)

  • Directly connected to the producer to send the production message directly to the message queue, and the consumer directly obtains it from the message queue for consumption
  • work queue Work queue, in this scenario, the producer sends the message to the message queue, and multiple consumers obtain the message according to the load balancing rules (default polling), and the message can only be consumed once, (similar to nucleic acid, I = message , worker=consumer, I can only do nucleic acid at one worker)
  • Fanout broadcast, like work queue, one producer corresponds to multiple consumers, but the difference is that fanout sends a message to all consumers to realize the broadcast function (everyone can hear it). It should be noted that from fanout to the following usage, rabbitmq does not directly send messages to the message queue, but sends them to the switch first, and the switch performs routing rule matching
  • Direct routing, direct can also be a one-to-many relationship, but whether to send or not depends on whether the route made by the producer matches the consumer, (for example, the producer sends a message and attaches a parameter 1 , which means that the parameter specified by the consumer to receive the message is also 1, if it is 0 or 2, the message will not be received)
  • Topic routing (upgraded version), its principle is the same as direct, but the routing method of direct is relatively rigid and not flexible enough. Topic introduces wildcards to match routing, which can make communication more flexible.

Java code operation rabbitmq (including several working scenarios)

Note: docker deployment of rabbit is more cumbersome and is placed at the end of the article. Students who need it should go to docker deployment first.

新建Maven项目 导入 rabbitmq依赖
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.7.2</version>
        </dependency>


    /**
     * 创建 mq 连接工厂
     * @return
     */
    public static Connection getConnection(){
    
    
        // 创建连接mq的连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq 的主机  端口  虚拟机名
        connectionFactory.setHost("mq所在的服务主机");
        //rabbitmq  通讯端口
        connectionFactory.setPort(5672);
        //虚拟机名
        connectionFactory.setVirtualHost("/test-v");

        //设置虚拟主机对应的用户名 密码 
        connectionFactory.setUsername("test-user");
        connectionFactory.setPassword("123");
        Connection connection = null ;
        try {
    
    
            connection = connectionFactory.newConnection();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return connection;
    }

Working scenario 1: direct connection

In this mode, the producer sends messages directly to the consumer



//先启动消费者 实时监听,然后生产者发送消息 ,可以看到消费者是可以立即获取到消息的。

//生产者代码
    public static void main(String[] args) throws Exception {
    
    
        // 获取mq 通讯连接
        Connection connection = getConnection();
        //获取通道
        Channel channel = connection.createChannel();

        //通道绑定一个消息队列
        // 参数说明: 队列名称(不存在则创建)  是否持久化(true重启服务后仍然存在) 是否独占序列(true 其它连接不允许访问)
        //                                              消息被消费完是否删除(true) 其它(参数) 暂时用不到
        channel.queueDeclare("hello",true,false,false,null);

        //发布消息
        // 参数说明: 交换机名称  队列名称  传递消息额外设置  参数具体内容
        // 注:第一个参数交换机为 "" 此模型为直连模型 直接由生产者发送至消费者,不存在交换机。
        channel.basicPublish("","hello",null,"hello world".getBytes(StandardCharsets.UTF_8));

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

//消费者代码

    public static void main(String[] args) throws Exception {
    
    
        // rabbit获取连接
        Connection connection = RabbitDemo.getConnection();
        // 通道
        Channel channel = connection.createChannel();

        //绑定队列
        //参数说明:1 队列名称(与生产者对应)   2 是否持久化(与生产者对应)  3 是否独占序列(必须与生产者对应)
        // 4 消息被消费完是否删除(与生产者对应)  5 其它(参数)暂时用不到
        channel.queueDeclare("hello",true,false,false,null);

        //持续监听绑定队列 然后消费消息
        // 参数说明:1 队列名称   2 开启消息自动确认机制  false的话消费完该消息还会存储在队列中 不消失
        channel.basicConsume("hello",true,new DefaultConsumer(channel){
    
    
            @Override  // 参数说明    其它都暂时用不到   最后一个是收到生产者发送消息的byte流
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                System.out.println("body is "+ new String(body));
            }
        });
    }


Factory scene two: workqueue

This working scenario is realized by one producer corresponding to multiple consumers, and multiple consumers listen to the producer at the same time, but the message can only be consumed by one consumer

//生产者 此处生产者与第一种模式一致 循环发送10次数据
    public static void main(String[] args) {
    
    
        Connection connection = getConnection();
        try {
    
    
            Channel channel = connection.createChannel();
            //通道绑定一个消息队列
            channel.queueDeclare("hello",true,false,false,null);
            //发布消息
            for (int i = 0; i < 10; i++) {
    
    
                channel.basicPublish("","hello",null,("hello world"+i).getBytes(StandardCharsets.UTF_8));
            }
            channel.close();
            connection.close();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
    //创建两个消费者
        public static void main(String[] args) {
    
    
        Connection connection = getConnection();
        Channel channel = null;
        try {
    
    
            channel = connection.createChannel();
            channel.queueDeclare("hello",true,false,false,null);

            channel.basicConsume("hello",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));
                }
            });
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

//第二个消费者
    public static void main(String[] args) {
    
    
        Connection connection = getConnection();
        Channel channel = null;
        try {
    
    
            channel = connection.createChannel();
            channel.queueDeclare("hello",true,false,false,null);

            channel.basicConsume("hello",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));
                }
            });
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }


To verify at this time, the two consumers will first start monitoring and then start the producer to send data to check the consumer consumption

insert image description here
insert image description here
I can clearly see that two consumers consume messages alternately, but at this time, I have a question. If my first consumer processes business much slower than the second consumer, will it still be alternate consumption?

// 第一个消费者睡眠2s 重启两个消费者 查看消费状况

                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                    System.out.println("我是第一个消费者:"+new String(body));
                    try {
    
    
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
   

The final consumption result is still five per person, but it can be clearly seen that the second consumer has already consumed five, and the first consumer is still consuming one by one, that is to say, the consumption strategy at this time is to Tasks are distributed equally among all listening consumers .

So how is the code set up? Let’s take a look at the
insert image description here
second parameter of the basicConsume function in a picture. Here, the parameters are explained in the first working scene. The second parameter is whether to enable automatic confirmation of messages . This automatic confirmation mechanism means that , when the code executes this sentence, that is, when the message is monitored, it will be confirmed immediately, telling mq that I have confirmed that I have received it. In this case, mq will not control the execution logic of the consumer, that is, whether your execution is completed or not has nothing to do with me. , I continue to poll for published messages for all listening consumers.
This method may cause some problems. For example, the second consumer gets five messages, and hangs up after executing the third one. At this time, the last two messages will be lost. Then there must be another solution, that is, the consumer will only confirm receipt of the message after the execution is completed , so what is the consumption situation at this time, let's look at the code.

// 消费者一   修改部分一一注释

   public static void main(String[] args) {
    
    
        Connection connection = getConnection();
        try {
    
    
            Channel channel = connection.createChannel();
            channel.queueDeclare("hello",true,false,false,null);
            // 此处设置每次限制消费一
            channel.basicQos(1);
            //            第二个参数修改为false取消自动确认
            channel.basicConsume("hello",false,new DefaultConsumer(channel){
    
    
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                    System.out.println("我是第一个消费者:"+new String(body));
                    try {
    
    
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    //此处设置手动确认 
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            });
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

//消费者二 与一类似

  public static void main(String[] args) {
    
    
        Connection connection = getConnection();
        try {
    
    
            Channel channel = connection.createChannel();
            channel.queueDeclare("hello",true,false,false,null);
            channel.basicQos(1);

            channel.basicConsume("hello",false,new DefaultConsumer(channel){
    
    
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                    System.out.println("我是第二个消费者:"+new String(body));
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            });
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

insert image description here
insert image description here

We can see that consumer 2 has consumed nine items, and consumer 1 has consumed one item. At this time, we check mq and find that the message in the queue is 0, indicating that the consumption is successful.

Broadcast working mode (fanout)

As the name of broadcasting implies, one producer corresponds to multiple consumers, and a message will be heard by all consumers listening to the producer , and the code is directly uploaded

// 生产者 代码
    public static void main(String[] args) throws IOException {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();

        //此处通道调用队列方法改为调用交换机
        // 参数  交换机名   交换机类型
        channel.exchangeDeclare("test-exchange","fanout");

        for (int i = 0; i < 10; i++) {
    
    
            channel.basicPublish("test-exchange","",null,("hello world"+i).getBytes(StandardCharsets.UTF_8));
        }
    }// 消费者代码
    public static void main(String[] args) throws IOException {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();
        // 通道绑定交换机, 名字与类型对应上
        channel.exchangeDeclare("test-exchange","fanout");

        //获取临时队列
        String queue = channel.queueDeclare().getQueue();

        //参数说明    临时队列名  交换机名,  路由参数(此时用不到,后面会讲)
        channel.queueBind(queue,"test-exchange","");

        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("消费者一:"+new String(body));
            }
        });

    }
    public static void main(String[] args) throws IOException {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();
        // 通道绑定交换机, 名字与类型对应上
        channel.exchangeDeclare("test-exchange","fanout");

        //获取临时队列
        String queue = channel.queueDeclare().getQueue();

        //参数说明    临时队列名  交换机名,  路由参数(此时用不到,后面会讲)
        channel.queueBind(queue,"test-exchange","");

        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("消费者二:"+new String(body));
            }
        });

    }


insert image description here

Realize the broadcasting function. Two consumers are tested here, and multiple consumers can also be implemented.

Direct Topic

The two working modes are explained together here, because the principles are the same. The
second parameter of basicPublish is the routing key. The key set by the producer needs to correspond to all broadcast consumers before the message will be sent, otherwise it will not be sent.
insert image description here

// 生产者

    public static void main(String[] args) throws Exception {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();

        //此处交换机类型 设置direct
        channel.exchangeDeclare("test-direct","direct");

        for (int i = 0; i < 10; i++) {
    
    
  //  第二个参数 路由key
            channel.basicPublish("test-direct","key",null,("hello world"+i).getBytes(StandardCharsets.UTF_8));
        }
        channel.close();
        connection.close();
    }


//消费者一
    public static void main(String[] args) throws IOException {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();
        // 通道绑定交换机
        channel.exchangeDeclare("test-direct","direct");

        //获取临时队列
        String queue = channel.queueDeclare().getQueue();

        //参数说明    临时队列名  交换机名,  路由key 与生产者对应则可以接收数据
        channel.queueBind(queue,"test-direct","key");

        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("消费者一:"+new String(body));
            }
        });
    }

//消费者二
    public static void main(String[] args) throws IOException {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();
        
        channel.exchangeDeclare("test-direct","direct");

        //获取临时队列
        String queue = channel.queueDeclare().getQueue();

        //  此处设置key1  与key不对应 则此消费者接收不到消息
        channel.queueBind(queue,"test-direct","key1");

        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("消费者二:"+new String(body));
            }
        });

    }


注:此处   可以设置多个 根据场景随意设置
     channel.queueBind(queue,"test-direct","key1");
     channel.queueBind(queue,"test-direct","key2");
     channel.queueBind(queue,"test-direct","key3");
     

topic

// topic 与fanout一致 就是routerkey 由具体的 变成抽象的 我们看代码

//生产者 
    public static void main(String[] args) throws Exception {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();


        channel.exchangeDeclare("test-topic","topic");
//此处路由key设置 test.a  那么 消费者的key需要设置test.a 或者 test.*
//  消费者设置test.* 则可以接收所有的test.加随意字符
		String routerKey = "test.a";
        for (int i = 0; i < 10; i++) {
    
    

            channel.basicPublish("test-direct",routerKey,null,("hello world"+i).getBytes(StandardCharsets.UTF_8));
        }
        channel.close();
        connection.close();

    }
//消费者代码

    public static void main(String[] args) throws IOException {
    
    
        Connection connection = getConnection();
        Channel channel = connection.createChannel();
        // 通道绑定交换机, 名字与类型对应上
        channel.exchangeDeclare("test-topic","topic");

        //获取临时队列
        String queue = channel.queueDeclare().getQueue();

        //                   第三个参数 路由key  需要与生产者的通配符匹配
        //                    具体通配符大家可以网上搜索,此处不作详细解答
        //          使用通配符就是为了使得多个交换机多个消费匹配起来更加的//方便灵活
        channel.queueBind(queue,"test-topic","test.*");

        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("消费者一:"+new String(body));
            }
        });
    }



Linux docker stand-alone deployment rabbitmq

Preparation environment: centos7
docker (if you haven’t installed it, you can search it, the command installation is very simple)

1. Image pull docker pull rabbitmq:management (it must be the version type of management, this is with visualization tools, and can be managed through the web) 2.
Create a container

//                                用户名              密码
docker run -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456 
//    容器名                                         镜像名字 : 镜像版本
--name test-rabbit -p 15672:15672 -p 5672:5672 -d rabbitmq:management


3, docker ps to check whether the container is running, if you enter ip:15672 in the address bar, it will open the rabbitmq management page

4. Create users and virtual hosts

Enter the name password when you just created the container
insert image description here
to enter the management page and we create a user

Click admin -> users -> add a user -> add user
to fill in the user name and password tags is the corresponding authority of the user administrator is the super administrator authority
insert image description here

We can see that the third column of the created user can access virtual host has no value, that is to say, the newly created user cannot operate any virtual host. Next, we create a virtual host to bind the user. Between virtual hosts It is a production and development environment that is isolated from each other and does not affect each other.

insert image description here
It is necessary to create a virtual machine and assign it to users. Click on the virtual machine and fill in a virtual machine name.

insert image description here
After the creation is complete, click the user name to enter the configuration page and assign the virtual machine just now to the user.
insert image description here
Click to select the virtual machine and confirm, and you can see the mark 3 appear.

insert image description here

Remark

Everyone mainly understands the working methods and working scenarios. If you have a suitable business, you can use it. You don’t need to lock up the code. If you have any questions, you can private me. I will write an article later, springboot operates rabbit

Guess you like

Origin blog.csdn.net/pgcdnameming/article/details/126002541