菜鸟的RabbitMQ学习总结

说明

更新时间:2020/9/13 23:32,更新到了rabbitmq集群
更新时间:2020/9/10 21:26,更新到了第五种模型-topic
更新时间:2020/9/9 22:46,更新到了第一种模型-直连

本文主要对RabbitMQ进行学习与记录,本文会持续更新,不断地扩充

本文仅为记录学习轨迹,如有侵权,联系删除

一、概念

(1)什么是RabbitMQ

具体的概念可以自行访问官网:https://www.rabbitmq.com/#getstarted

简单讲一下RabbitMQ的基本概念,RabbitMQ是目前非常热门的一款消息中间件,它可以用来做消息队列(Message Queue),是一种跨进程、异步的通信机制,用于上下游传递消息。由消息系统来确保消息的可靠传递。它采用AMQP(Advanced Message Queuing Protocol)高级消息队列协议:高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。

(2)7种消息模型

重点讲一下里面的7种模型,官网上是英文的,这里直接翻译成中文,7种模型能干嘛里面已经写得很清楚了,看一下就可以了
在这里插入图片描述

(3)生产者和消费者模型

在这里插入图片描述

Server主机地址
Virtuol Host虚拟主机,这个就像数据库里面的一个一个的库,一个项目对应一个虚拟主机,虚拟主机之间相互隔离
Exchange交换机,如上面的7种模型,第3、4、5种消息模型中,紫色的那个就是交换机,生产者将数据丢给交换机,再由交换机分发出去
Quene消息队列,生产者可以直接将数据丢给消息队列,消费者再去队列拿消息,也可以经过交换机将数据丢给消息队列

二、RabbitMQ安装

关于安装可以看一下本人的另一篇博文:菜鸟学习Docker实例,里面有RabbitMQ的安装,采用docker一键安装,简单方便

三、创建MQ虚拟主机以及用户

先进入rabbitmq的界面,为项目创建一个虚拟主机
在这里插入图片描述
创建用户名和密码
在这里插入图片描述

此时创建的用户还没有授权,点击用户进行授权
在这里插入图片描述

将创建的虚拟主机授权给用户即可
在这里插入图片描述
至此创建MQ虚拟主机以及用户结束。

三、第一种模型-直连

在这里插入图片描述
p表示生产者,将数据丢进消息队列,c表示消费者,消费消息队列里面的消息,下面直接进入实战

创建一个maven项目,引入相关的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zsc</groupId>
    <artifactId>rabbitmq01</artifactId>
    <version>1.0-SNAPSHOT</version>

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

        <!--rabbitmq依赖-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>


    </dependencies>


</project>

创建生产者实体类,并创建一个消息队列,往里面传值

package com.zsc.rabbitmq01;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Provider
 * @Description : 消息队列第一种模型:点对点,生成者
 * @Author : CJH
 * @Date: 2020-09-09 20:10
 */
public class Provider {
    private final static String QUEUE_NAME = "hello";

    //生产者发送消息
    @Test
    public  void sendMessage() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定对应消息队列
        /**
         * 通道声明
         * 
         * 这里关于参数的说明
         * 参数1:队列名称,如果队列不存在则自动创建
         * 参数2:用于定义队列特性是否要持久化,true为是,false为否,如果为false,重启mq服务后,里面的消息会被清除,如果为true,队列不会被删除,
         * 但消息会被清空,它必须配合下面 channel.basicPublish发布消息时,将第三个参数设置为”MessageProperties.PERSISTENT_TEXT_PLAIN“才能实现队列和消息同时持久化
         * 
         * 参数3:exclusive 表示是否独占队列,true为是,false为否,一般为false,即该通道可以被共享
         * 参数4:autoDelete 是否在消费完成后自动删除队列,true为是,false为否
         * 参数5:额外的附加参数
         */
        /**注意:经常出错的地方,这里的queueDeclare所有的参数设置必须跟消费者的queueDeclare一致,这样才能准确的消费哪一个队列,否则会报错**/
        channel.queueDeclare("hello",false,false,false,null);


        

        //发布消息
        /**
         * 这里关于参数的说明
         * 参数1:交换机名称
         * 参数2:队列名称
         *
         * 参数3:传递消息额外设置,配合上面的channel.queueDeclare,设置为MessageProperties.PERSISTENT_TEXT_PLAIN,可实现队列和消息同时持久化
         * 参数4:消息的具体内容
         */
        channel.basicPublish("","hello", MessageProperties.PERSISTENT_TEXT_PLAIN,"hello world".getBytes());
        System.out.println("向hello消息队列发送消息成功");

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




}


创建消费者,只有队列里面有消息就进行消费

package com.zsc.rabbitmq01;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第一种模型:点对点,消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定对应消息队列
        /**
         * 通道声明
         *
         * 这里关于参数的说明
         * 参数1:队列名称,如果队列不存在则自动创建
         * 参数2:用于定义队列特性是否要持久化,true为是,false为否,如果为false,重启mq服务后,里面的消息会被清除,如果为true,队列不会被删除,
         * 但消息会被清空,它必须配合下面 channel.basicPublish发布消息时,将第三个参数设置为”MessageProperties.PERSISTENT_TEXT_PLAIN“才能实现队列和消息同时持久化
         *
         * 参数3:exclusive 表示是否独占队列,true为是,false为否,一般为false,即该通道可以被共享
         * 参数4:autoDelete 是否在消费完成后自动删除队列,true为是,false为否
         * 参数5:额外的附加参数
         */
        /**注意:这里必须跟生产者的通道声明一致才不会报错**/
        channel.queueDeclare("hello",false,false,false,null);


        channel.basicQos(0, 1, false);//这样RabbitMQ就会使得每个Consumer在同一个时间点最多处理一个Message。
        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));
            }
        });

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


}

测试,生产者先往消息队列存放数据,运行3次,存放三条数据
在这里插入图片描述
消费者消费队列里面的消息
在这里插入图片描述

四、第二种模型-work

在这里插入图片描述
第二种模型相较于第一种模型,解决了当消息队列的消息过多的情况,单消费者消费速率有限导致的消息堆积的问题。

生产者的创建,生产者的代码跟上面的第一种模型的代码基本差不多

package com.zsc.rabbitmq02;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Provider
 * @Description : 消息队列第一种模型:生成者
 * @Author : CJH
 * @Date: 2020-09-09 20:10
 */
public class Provider {

    //生产者发送消息
    @Test
    public  void sendMessage() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定对应消息队列
        /**
         * 通道声明
         *
         * 这里关于参数的说明
         * 参数1:队列名称,如果队列不存在则自动创建
         * 参数2:用于定义队列特性是否要持久化,true为是,false为否,如果为false,重启mq服务后,里面的消息会被清除,如果为true,队列不会被删除,
         * 但消息会被清空,它必须配合下面 channel.basicPublish发布消息时,将第三个参数设置为”MessageProperties.PERSISTENT_TEXT_PLAIN“才能实现队列和消息同时持久化
         *
         * 参数3:exclusive 表示是否独占队列,true为是,false为否,一般为false,即该通道可以被共享
         * 参数4:autoDelete 是否在消费完成后自动删除队列,true为是,false为否
         * 参数5:额外的附加参数
         */
        /**注意:经常出错的地方,这里的queueDeclare所有的参数设置必须跟消费者的queueDeclare一致,这样才能准确的消费哪一个队列,否则会报错**/
        channel.queueDeclare("work",true,false,false,null);

        //发布消息
        /**
         * 这里关于参数的说明
         * 参数1:交换机名称
         * 参数2:队列名称
         *
         * 参数3:传递消息额外设置,配合上面的channel.queueDeclare,设置为MessageProperties.PERSISTENT_TEXT_PLAIN,可实现队列和消息同时持久化
         * 参数4:消息的具体内容
         */

        for (int i = 0; i < 100; i++) {
            channel.basicPublish("","work", MessageProperties.PERSISTENT_TEXT_PLAIN,("this is "+i).getBytes());
            System.out.println("向hello消息队列发送消息成功");
        }

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

消费者有两个以上,但大部分的代码都是差不多的
消费者1

package com.zsc.rabbitmq02;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第二种模型:消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer1 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定对应消息队列
        /**
         * 通道声明
         *
         * 这里关于参数的说明
         * 参数1:队列名称,如果队列不存在则自动创建
         * 参数2:用于定义队列特性是否要持久化,true为是,false为否,如果为false,重启mq服务后,里面的消息会被清除,如果为true,队列不会被删除,
         * 但消息会被清空,它必须配合下面 channel.basicPublish发布消息时,将第三个参数设置为”MessageProperties.PERSISTENT_TEXT_PLAIN“才能实现队列和消息同时持久化
         *
         * 参数3:exclusive 表示是否独占队列,true为是,false为否,一般为false,即该通道可以被共享
         * 参数4:autoDelete 是否在消费完成后自动删除队列,true为是,false为否
         * 参数5:额外的附加参数
         */
        /**注意:这里必须跟生产者的通道声明一致才不会报错**/
        channel.queueDeclare("work",true,false,false,null);


        //表示通道一次只能消费一个消息
        channel.basicQos(1);

        /**
         * 参数1:队列名称
         * 参数2:消息自动确认
         * true:表示一接到消息,立刻向mq发送确认消息,不管你消息后,这条消息的有没有执行完,这样会有一个问题,瞬间接收到10条消息,但是只执行了4条消息后,突然宕机了,这样其余没来得及消费的6条消息就会丢失了
         * false:表示接到消息后不会立刻发送确认消息,需要人为发送确认消息,mq接到确认消息后才会消除队列里面的消息,这样就可以人为的做到在执行完消息后再发送确认消息,不会造成消息的丢失
         */
        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("消费者-1:"+new String(body));

                //手动确认消息  参数1:手动确认消息标识  参数2;false 每次确认一个
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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


}

消费者2

package com.zsc.rabbitmq02;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第二种模型:消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer2 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定对应消息队列
        /**
         * 通道声明
         *
         * 这里关于参数的说明
         * 参数1:队列名称,如果队列不存在则自动创建
         * 参数2:用于定义队列特性是否要持久化,true为是,false为否,如果为false,重启mq服务后,里面的消息会被清除,如果为true,队列不会被删除,
         * 但消息会被清空,它必须配合下面 channel.basicPublish发布消息时,将第三个参数设置为”MessageProperties.PERSISTENT_TEXT_PLAIN“才能实现队列和消息同时持久化
         *
         * 参数3:exclusive 表示是否独占队列,true为是,false为否,一般为false,即该通道可以被共享
         * 参数4:autoDelete 是否在消费完成后自动删除队列,true为是,false为否
         * 参数5:额外的附加参数
         */
        /**注意:这里必须跟生产者的通道声明一致才不会报错**/
        channel.queueDeclare("work",true,false,false,null);

        //表示通道一次只能消费一个消息
        channel.basicQos(1);

        /**
         * 参数1:队列名称
         * 参数2:消息自动确认
         * true:表示一接到消息,立刻向mq发送确认消息,不管你消息后,这条消息的有没有执行完,这样会有一个问题,瞬间接收到10条消息,但是只执行了4条消息后,突然宕机了,这样其余没来得及消费的6条消息就会丢失了
         * false:表示接到消息后不会立刻发送确认消息,需要人为发送确认消息,mq接到确认消息后才会消除队列里面的消息,这样就可以人为的做到在执行完消息后再发送确认消息,不会造成消息的丢失
         */
        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(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("消费者-2:"+new String(body));

                //手动确认消息  参数1:手动确认消息标识  参数2;false 每次确认一个
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        channel.close();
//        connection.close();
    }


}

注意消费者代码里面的消息确认机制,为了实现"能者多劳",改为手动确认消息,同时在代码最后沉睡20秒,这样就可以持续监听队列了,测试的时候先运行两个消费者,再运行生产者,这样生产者一发送消息到队列,在20秒内,消费者监听到队列的消息,就可以进行消息的消费
在这里插入图片描述

五、第三种模型-publish

在这里插入图片描述
这种模型类似于广播模型,这里将消息全部存入交换机,再由交换机发到各个消息队列,经由消息队列发给各个消费者

这里的生产者需要用到交换机,与交换机进行绑定

package com.zsc.rabbitmq03;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Provider
 * @Description : 消息队列第三种模型:广播 生成者
 * @Author : CJH
 * @Date: 2020-09-09 20:10
 */
public class Provider {
    private final static String QUEUE_NAME = "hello";

    //生产者发送消息
    @Test
    public  void sendMessage() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //将通道声明为指定交换机  //参数1:交换机名称   参数2:交换机类型  fanout表示广播类型
        channel.exchangeDeclare("logs","fanout");

        //发送消息
        channel.basicPublish("logs","",null,"this is 广播类型".getBytes());

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

    }


}

这里的消费者代码也跟上面的有很大的区别,消费者需要绑定交换机和队列
消费者1

package com.zsc.rabbitmq03;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第三种模型:广播  消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer1 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定交换机
        channel.exchangeDeclare("logs","fanout");

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

        //绑定交换机和队列
        channel.queueBind(queue,"logs","");


        //消费消息
        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));
            }
        });



        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}

消费者2

package com.zsc.rabbitmq03;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第三种模型:广播  消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer2 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定交换机
        channel.exchangeDeclare("logs","fanout");

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

        //绑定交换机和队列
        channel.queueBind(queue,"logs","");


        //消费消息
        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));
            }
        });




        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}

运行的时候,只要生产者向交换机传递消息,那么交换机就会将消息同时发送给绑定的两个消费者
在这里插入图片描述

六、第四种模型-routing

在这里插入图片描述
第四种模型,路由模式,相较于第三种模型,功能可以说是在第三种的模型上加了个routingKey,生产者传递消息的时候带上一个routingKey,这样消费者只有带有相同的routingKey才会接收到消息,否则就不会接收到消息

生产者,生产消息时带上了一个routingKey

package com.zsc.rabbitmq04;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Provider
 * @Description : 消息队列第四种模型:路由 生成者
 * @Author : CJH
 * @Date: 2020-09-09 20:10
 */
public class Provider {
    private final static String QUEUE_NAME = "hello";

    //生产者发送消息
    @Test
    public  void sendMessage() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //将通道声明为指定交换机  //参数1:交换机名称   参数2:交换机类型  direct表示路由模式
        channel.exchangeDeclare("logs_direct","direct");

        //设置routingKey
        String routingKey = "info";

        //发送消息
        channel.basicPublish("logs_direct",routingKey,null,("this is 路由类型,routingKey为["+routingKey+"]").getBytes());

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

    }




}

消费者也带上一个routingKey,这样只有生产者与消费者的routingKey相同时才会接收到消息
消费者1

package com.zsc.rabbitmq04;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第四种模型:路由  消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer1 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定交换机
        channel.exchangeDeclare("logs_direct","direct");

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

        //绑定交换机和队列,设置routingKey为info,这样只要生产者发送的消息带有info的routingKey,它就会接收到
        channel.queueBind(queue,"logs_direct","info");


        //消费消息
        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));
            }
        });



        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}

消费者2

package com.zsc.rabbitmq04;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第四种模型:路由  消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer2 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定交换机
        channel.exchangeDeclare("logs_direct","direct");

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

        //绑定交换机和队列,设置routingKey为error,这样只要生产者发送的消息带有error的routingKey,它就会接收到
        channel.queueBind(queue,"logs_direct","error");


        //消费消息
        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));
            }
        });



        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}

测试,生产者的routingKey为info
在这里插入图片描述

七、第五种模型-topics

在这里插入图片描述
第五种模型为动态路由,说白了就是在第四种模型的基础上增加了通配符*和#,它可以做到routingKey的匹配

生产者的代码,将交换机类型改为topic

package com.zsc.rabbitmq05;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Provider
 * @Description : 消息队列第五种模型:动态路由 生成者
 * @Author : CJH
 * @Date: 2020-09-09 20:10
 */
public class Provider {
    private final static String QUEUE_NAME = "hello";

    //生产者发送消息
    @Test
    public  void sendMessage() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //将通道声明为指定交换机  //参数1:交换机名称   参数2:交换机类型  direct表示路由模式
        channel.exchangeDeclare("logs_topic","topic");

        //设置routingKey
        String routingKey = "user.add";

        //发送消息
        channel.basicPublish("logs_topic",routingKey,null,("this is 路由类型,routingKey为["+routingKey+"]").getBytes());

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

    }




}

消费者1

package com.zsc.rabbitmq05;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第五种模型:动态路由  消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer1 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定交换机
        channel.exchangeDeclare("logs_topic","topic");

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

        //绑定交换机和队列,设置routingKey为多个,这样只要生产者发送的消息带有对应的routingKey,它就会接收到
        channel.queueBind(queue,"logs_topic","person.*");
        channel.queueBind(queue,"logs_topic","user.*");


        //消费消息
        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));
            }
        });



        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}

消费者2

package com.zsc.rabbitmq05;

import com.rabbitmq.client.*;
import org.junit.jupiter.api.Test;

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

/**
 * @ClassName : Customer
 * @Description : 消息队列第五种模型:动态路由  消费者
 * @Author : CJH
 * @Date: 2020-09-09 21:57
 */
public class Customer2 {

    @Test
    public void receive() throws IOException, TimeoutException {
        //创建连接mq的工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置连接rabbitmq主机
        connectionFactory.setHost("39.96.22.34");
        //设置端口号
        connectionFactory.setPort(5672);//注意端口号是5672不是15672
        //设置虚拟主机
        connectionFactory.setVirtualHost("/test01");
        //设置访问的虚拟主机的用户名和密码
        connectionFactory.setUsername("user01");
        connectionFactory.setPassword("123");

        //获取连接对象
        Connection connection = connectionFactory.newConnection();

        //获取连接通道
        Channel channel = connection.createChannel();

        //通道绑定交换机
        channel.exchangeDeclare("logs_topic","topic");

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

        //绑定交换机和队列,设置routingKey为多个,这样只要生产者发送的消息带有对应的routingKey,它就会接收到
        channel.queueBind(queue,"logs_topic","person.*");
        channel.queueBind(queue,"logs_topic","student.*");


        //消费消息
        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));
            }
        });



        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}

由于生产者设置的交换机为topic,且routingKey为user.add,那么消费者1的routingKey有user.*,所以会匹配到,即会接收到消息,而消费者2没有user. *,所以匹配不到,即收不到消息
在这里插入图片描述

八、springboot整合rabbitmq

(1)第一种模型(hello)

pom引入坐标依赖,这里贴出本人正在使用的完整项目坐标依赖

    <dependencies>
        <!--rabbitmq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

整合后需要进行相关配置,这里的ip和用户密码本人给屏蔽了,给了个假的ip和密码

spring:
  application:
    #随便起个名字
    name: springboot_rabbitmq
  rabbitmq:
    #主机ip
    host: 139.86.131.20
    #用户名
    username: test01
    #密码
    password: 123456
    #虚拟主机
    virtual-host: /test01

这里配置好之后,它会自动注入一个模板RabbitTemplate方便我们的操作,注意,如果创建好生产者之后向队列发送消息,如果该队列提前不存在的话,消息会发送失败,所以需要提前创建消费者,让消费者提前创建好队列后,监听队列后,再生产者发送消息,所以先创建消费者

package com.zsc.rabbitmq01;

import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @ClassName : Customer
 * @Description : 消息队列第一种模型:点对点,消费者
 * @Author : CJH
 * @Date: 2020-09-13 14:43
 */
@Component//加入容器
//@RabbitListener(queuesToDeclare = @Queue(name = "person",durable = "false"))//durable持久化,除此外@Queue注解还可以配置其他参数
@RabbitListener(queuesToDeclare = @Queue("person"))//如果队列不存在则创建person队列,默认队列是持久化,非独占式的
public class Customer {

    //消费者如果监听到消息队列有消息传入,则会自动消费
    @RabbitHandler
    public void receive(String message){
        System.out.println("消费者接收到消息,message: "+message);
    }

}

创建生产者,用单元测试的方式创建方便演示

package com.zsc.rabbitmq01;

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @ClassName : Provider
 * @Description : 消息队列第一种模型:点对点,生成者
 * @Author : CJH
 * @Date: 2020-09-13 14:39
 */
@SpringBootTest
public class Provider {
    //自动配置好的mq模板
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    void test01(){
        //向person队列发送信息(注意:此时必须消费者提前创建好该队列)
        rabbitTemplate.convertAndSend("person","my name is Tom");
    }

}

启动生产者后,因为整个springboot启动,所以消费者会监听队列,然后生产者发送消息,消费者获取消息
在这里插入图片描述

(2)第二种模型(work)

生产者

@Component//加入容器
public class Customer2 {

    //消费者如果监听到消息队列有消息传入,则会自动消费
    @RabbitListener(queuesToDeclare = @Queue("work"))//如果队列不存在则创建person队列,默认队列是持久化,非独占式的
    public void receive1(String message){
        System.out.println("消费者1接收到消息,message: "+message);
    }

    //消费者如果监听到消息队列有消息传入,则会自动消费
    @RabbitListener(queuesToDeclare = @Queue("work"))//如果队列不存在则创建person队列,默认队列是持久化,非独占式的
    public void receive2(String message){
        System.out.println("消费者2接收到消息,message: "+message);
    }

}

消费者

@SpringBootTest
public class Provider {
    //自动配置好的mq模板
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    void test01(){
        //向队列发送信息(注意:此时必须消费者提前创建好该队列)
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("work","("+i+")第二种模型:work");
        }
    }

}

注意,这里配置的第二种队列是采用轮询的方式,所有的消息平均分给各个消费者,上面创建了两个消费者,所以它们会各自消费一半的消息,后期可以改为能者多劳"的模式,执行快的,消息执行得多这样的方式
在这里插入图片描述

(3)第三种模型(fanout)

第三种模型是交换机的广播模式,先创建消费者,注意这里需要给消费者用注解将交换机和队列进行绑定

@Component//加入容器
public class Customer3 {

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//不写名字默认创建临时队列
                    exchange = @Exchange(value = "school",type = "fanout")//绑定了一个名为school的交换机,同时类型为fanout广播类型
            )
    })
    public void receive1(String message) {
        System.out.println("消费者1接收到消息,message: " + message);
    }


    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//不写名字默认创建临时队列
                    exchange = @Exchange(value = "school",type = "fanout")//绑定了一个名为school的交换机,同时类型为fanout广播类型
            )
    })
    public void receive2(String message) {
        System.out.println("消费者2接收到消息,message: " + message);
    }

}

生产者

@SpringBootTest
public class Provider {
    //自动配置好的mq模板
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    void test01(){
        //向队列发送信息(注意:此时必须消费者提前创建好该队列)
        rabbitTemplate.convertAndSend("school","","第三种模型:fanout");
    }

}

此时,生产者发送一条数据,所有的消费者都会收到消息
在这里插入图片描述

(4)第四种模型

消费者

**@Component//加入容器
public class Customer4 {

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//不写名字默认创建临时队列
                    exchange = @Exchange(value = "theKey",type = "direct"),//绑定了一个名为theKey的交换机,同时类型为direct路由类型
                    key = {"key1","key2","key3"}
            )
    })
    public void receive1(String message) {
        System.out.println("消费者1接收到消息,message: " + message);
    }


    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//不写名字默认创建临时队列
                    exchange = @Exchange(value = "theKey",type = "direct"),//绑定了一个名为theKey的交换机,同时类型为direct路由类型
                    key = {"key1"}
            )
    })
    public void receive2(String message) {
        System.out.println("消费者2接收到消息,message: " + message);
    }

}**

生产者

@SpringBootTest
public class Provider {
    //自动配置好的mq模板
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    void test01(){
        //向队列发送信息(注意:此时必须消费者提前创建好该队列)
        rabbitTemplate.convertAndSend("theKey","key3","第四种模型:direct");
    }

}

生产者发送消息,routingKey为key3,此时,只有具有key3的消费者才能接收到数据
在这里插入图片描述

(5)第五种模型(topic)

消费者

@Component//加入容器
public class Customer5 {

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//不写名字默认创建临时队列
                    exchange = @Exchange(value = "theTopic",type = "topic"),//绑定了一个名为theTopic的交换机,同时类型为topic订阅发布类型
                    key = {"user.*","person.*"}
            )
    })
    public void receive1(String message) {
        System.out.println("消费者1接收到消息,message: " + message);
    }


    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,//不写名字默认创建临时队列
                    exchange = @Exchange(value = "theTopic",type = "topic"),//绑定了一个名为theTopic的交换机,同时类型为topic订阅发布类型
                    key = {"user.*"}
            )
    })
    public void receive2(String message) {
        System.out.println("消费者2接收到消息,message: " + message);
    }

}

生产者

@SpringBootTest
public class Provider {
    //自动配置好的mq模板
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    void test01(){
        //向队列发送信息(注意:此时必须消费者提前创建好该队列)
        rabbitTemplate.convertAndSend("theTopic","user.add","第五种模型:topic");
    }

}

测试
在这里插入图片描述

九、rabbitmq集群

前言:这里采用docker来进行rabbitmq集群的搭建,主要是采用”一主二从“的方式进行搭建,即一个主节点,两个从节点

(1)常规集群的搭建

这种常规的集群搭建并不是一种高可用的集群,为什么这么说,可以看一下下面的架构图
在这里插入图片描述
一个主节点,两个从节点,消费者均可以从任意节点消费消息,如果主节点添加有新数据,从节点那边都会显示该数据,但只是从节点通过主节点的数据得知有这些数据,所以从表面上来讲数据像是同步的,甚至从节点的数据变化,3个节点都会同步变化,但是一旦主节点挂掉后,从节点相关数据也跟着挂掉。

所以说白了就是,3个节点的数据变化是同步,但是主节点挂掉后,从节点的对应数据也会消失,这种集群不是高可用的,但可以用分担一台服务器上的rabbitmq的压力。

挂载文件准备
下面开始搭建集群,首先需要在服务器上创建3个文件夹用与挂载3个rabbitmq镜像实例的数据

mkdir rabbitmq
cd rabbitmq
mkdir rabbitmq01 rabbitmq02 rabbitmq03

创建的3个文件如下
在这里插入图片描述

镜像实例创建

docker run -d --hostname rabbitmq01 --name rabbitmqCluster01 -v $PWD/rabbitmq/rabbitmq01:/var/lib/rabbitmq -p 15672:15672 -p 5672:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' rabbitmq:management

docker run -d --hostname rabbitmq02 --name rabbitmqCluster02 -v $PWD/rabbitmq/rabbitmq02:/var/lib/rabbitmq -p 15673:15672 -p 5673:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie'  --link rabbitmqCluster01:rabbitmq01 rabbitmq:management

docker run -d --hostname rabbitmq03 --name rabbitmqCluster03 -v $PWD/rabbitmq/rabbitmq03:/var/lib/rabbitmq -p 15674:15672 -p 5674:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie'  --link rabbitmqCluster01:rabbitmq01 --link rabbitmqCluster02:rabbitmq02  rabbitmq:management

部分参数解释:
这里重点讲一下erlang.cookie,它是erlang实现分布式的必要文件,erlang分布式的每个节点上要保持相同的.erlang.cookie文件,同时保证文件的权限是400。

-e RABBITMQ_ERLANG_COOKIE=‘rabbitmqCookie’这个命令是为了让每个节点上要保持相同的.erlang.cookie文件
–link rabbitmqCluster01:rabbitmq01docker run --link可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量

创建成功后,已经成功运行起来了
在这里插入图片描述
同时可以访问
在这里插入图片描述

配置主节点

进入主节点镜像实例: docker exec -it rabbitmqCluster01 bash

进入容器后,分别输入下面命令

rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit

运行截图
在这里插入图片描述
配置第一个从节点

进入主节点镜像实例: docker exec -it rabbitmqCluster02 bash

进入容器后,分别输入下面命令

rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit

运行截图
在这里插入图片描述

配置第二个从节点

进入主节点镜像实例: docker exec -it rabbitmqCluster03 bash

进入容器后,分别输入下面命令

rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit

运行截图
在这里插入图片描述

配置完之外,可以登录进主节点看一下rabbitmq,会有从节点信息
在这里插入图片描述

集群测试
像主节点发送一条消息,其他从节点也会有相应消息
在这里插入图片描述
从节点消费该消息,其他节点的消息也显示被消费了
在这里插入图片描述
注意:当向任意一个节点发送消息后,其他的节点都会消息同步,但这种同步不是真正意义上的同步,因为如果那个发送消息的节点挂掉后,其他的节点的对应的这个队列的消息都会挂掉。

挂掉的一个节点如下
在这里插入图片描述

(2)搭建高可用的镜像集群

RabbitMQ镜像策略set_policy

#设置策略的格式如下
rabbitmqctl set_policy [-p <vhost>] [--priority <priority>] [--apply-to <apply-to>] <name> <pattern>  <definition>

#清除
rabbitmqctl clear_policy [-p <vhost>] <name>

#查看
rabbitmqctl list_policies [-p <vhost>]

部分参数的解读

ha-mode:策略键
1.all 队列镜像在群集中的所有节点上。当新节点添加到群集时,队列将镜像到该节点
2.exactly 集群中的队列实例数。
3.nodes 队列镜像到节点名称中列出的节点。

ha-sync-mode:队列同步
1.manual手动<默认模式>.新的队列镜像将不会收到现有的消息,它只会接收新的消息。
2.automatic自动同步.当一个新镜像加入时,队列会自动同步。队列同步是一个阻塞操作。

具体的可以参考这篇博文:https://www.jianshu.com/p/f81d62a8de02

设置高可用集群
可以通过设置镜像策略来实现集群的高可用,即各个节点的数据只要有更新就在各个节点上复制一份镜像,这样就做到真正意义上的同步,即使其中一个节点挂掉,其他节点照样可以使用

在上面的常规集群的基础上进行改造,进入任意一个节点的容器里面(以第一个主节点为例)

sudo docker exec -it rabbitmqCluster01 bash

输入命令,设置镜像策略

rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

这个命令的意思是:将所有队列设置为镜像队列,即队列会被同步复制到各个节点,"^“表示匹配所有的镜像队列,如果改为” ^hello"则表示匹配所有以hello结尾的队列,即以hello结尾的队列将会被同步复制到各个节点。

设置完之后的截图
在这里插入图片描述

接下来可以通过java向其中一个从节点发送消息,之后可以rabbitmq上面的队列多了+2标志,同时还有ha-all标志
在这里插入图片描述

此时向一个从节点发送消息后,再将该节点挂掉,依然能从其他节点消费该消息
在这里插入图片描述
在这里插入图片描述

注意:假设向从节点1发送消息,当从节点1挂掉后,依然可以从其他节点进行消息消费,但是如果从节点1因为挂掉了所以访问不了,所以当有请求访问从节点1的时候会报错,所以这还不是真正意义上的高可用,真正意义上的高可用应该是配合nginx服务器进行部署,通过代理的方式,将请求合理的分发给各个节点,当有一个节点挂掉后,nginx可以检测到,并且不会向已经挂掉的节点分发请求,而是向其他两个没挂掉的节点分发请求,即做到即使有任一节点挂掉后,也不会影响整个服务的正常运行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值