RabbitMQ实战教程笔记

什么是MQ

MQ就是消息队列,通过典型的生产者和消费者模型,生产者不停的向消息队列中生产消息,消费者不停的从消息队列中获取消息。因为消息的生产与消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,轻松实现系统间解耦合。

不同MQ的特点

**ActiveMQ:**是Apache出品的,它是一个完全支持JMS规范的消息中间件,丰富的API,多种集群架构模式让ActiveMQ成为业界老牌的消息中间件,在中小型企业颇受欢迎。但是它的吞吐量低,性能不高

**Kafka:**也是Apzche出品的,主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的是用于日志收集与传输,不支持事务,对于消息的重复、丢失、错误没有严格的要求。适合产生大量数据的互联网服务的数据收集业务。

**RocketMQ:**是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路源于Kafka,但它并没有进行copy,它对消息的可靠传输及事务做了优化。

**RabbitMQ:**是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现,AMQP主要特征是面向消息、队列、路由(包括点对点、发布订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性、可靠性要求很高的场景,对性能和吞吐量的要求还在其次

安装RabbitMQ

1、docker pull rabbitmq:3-management(3-management表示带web管理界面的)

2、运行容器

#5672是服务端口;15672是web页面的访问端口,如果不设置用户名密码,默认是guest;25672是集群端口
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq01 \

-e RABBITMQ_DEFAULT_USER=xxx \
    
-e RABBITMQ_DEFAULT_PASS=xxx \
    
rabbitmq:3-management
   

3、在浏览器栏访问IP地址:15672 可以访问到RabbitMQ的登录官网。

简单队列

(P) -> [|||] -> (C)

没有exchange交换机,一个生产者对应一个消费者,直接将消息放入消息队列,消费者一旦监听到队列中有消息,就会去消费。

**缺点:**如果消费者处理过慢,就会导致消息的堆积。

工具类

public class RabbitMQUtils {
     

    public static Connection getConnection(){
     
        try {
     
            //创建mq的连接工厂对象
            ConnectionFactory connectionFactory = new ConnectionFactory();
            //设置连接mq的主机
            connectionFactory.setHost("xx.xx.xx.xx");
            //设置端口号
            connectionFactory.setPort(5672);
            //设置连接哪个虚拟主机
            connectionFactory.setVirtualHost("/ems");
            //设置访问虚拟主机的用户名与端口号
            connectionFactory.setUsername("ems");
            connectionFactory.setPassword("xxx");
            return connectionFactory.newConnection();
        } catch (Exception e) {
     
            e.printStackTrace();
        }
        return null;
    }

    public static void closeConnectionAndChannel(Connection connection, Channel channel){
     
        try {
     
            if (channel != null){
     
                channel.close();
            }
            if (connection != null){
     
                connection.close();
            }
        }catch (Exception e){
     
            e.printStackTrace();
        }
    }
}

生产者

public class Provider {
     

    /**
     * 生产消息
     */
    @Test
    public void sendMessage() throws IOException, TimeoutException {
     
        //获取连接对象
        Connection connection = RabbitMQUtils.getConnection();
        //获取连接中的通道
        Channel channel = connection.createChannel();

        /**
         * 通道绑定对应的消息队列
         * 参数一:队列名称
         * 参数二:队列是否持久化,如果为true,当RabbitMQ重启过后,队列不会被清除,但是队列中的消息会被清除
         * 参数三:队列是否独占通道
         * 参数四:是否在消费完队列并且断开连接后,将队列删除
         * 参数五:附加参数
         */
        channel.queueDeclare("hello",true,false,false,null);

        /**
         * 发送消息
         * 参数一:交换机
         * 参数二:队列的名称
         * 参数三:传递消息额外设置,MessageProperties.PERSISTENT_TEXT_PLAIN表示队列中的消息也进行持久化
         * 参数四:消息的具体内容
         */
        channel.basicPublish("","hello", MessageProperties.PERSISTENT_TEXT_PLAIN,"hello rabbitmq,我来了".getBytes());

        //关闭通道、连接
        RabbitMQUtils.closeConnectionAndChannel(connection,channel);
    }
}

消费者

/**
 * 不要用Junit测试来实现消息消费,因为不支持多线程
 */
public class Consumer {
     
    public static void main(String[] args) throws IOException, TimeoutException {
     
        //获取连接对象
        Connection connection = RabbitMQUtils.getConnection();
        //获取连接中的通道
        Channel 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));
            }
        });
    }
}

工作队列

RabbitMQ实战教程笔记_第1张图片

没有交换机,一个生产者对应多个消费者,默认是平均分配的形式来进行消费

缺点:(默认是采用自动确认机制)如果一个消费者消费过慢也会导致整体效率的低下

生产者

public class Provider {
     

    @Test
    public void sendMessage() throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();

        //绑定队列
        channel.queueDeclare("work",true,false,false,null);
        for (int i = 0; i < 10; i++) {
     
            //生产消息
            channel.basicPublish("","work", null,(i + "你好,work queue").getBytes());
        }

        RabbitMQUtils.closeConnectionAndChannel(connection,channel);
    }
}

消费者1

public class Consumer1 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        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(3000);
                }catch (Exception e){
     
                    e.printStackTrace();
                }
                System.out.println("消费者1:"+new String(body));
            }
        });
    }
}

消费者11你好,work queue
消费者13你好,work queue
消费者15你好,work queue
消费者17你好,work queue
消费者19你好,work queue

消费者2

public class Consumer1 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        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));
            }
        });
    }
}

消费者22你好,work queue
消费者24你好,work queue
消费者26你好,work queue
消费者28你好,work queue
消费者210你好,work queue

手动确认机制

自动确认机制一般不用,因为消息一旦经消费者确认过后,如果此时消费者宕机了,那么原来确认过的消息还未被消费,却已经丢失了。

手动确认机制可以达到能者多劳的地步

public class Consumer1 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        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(3000);
                }catch (Exception e){
     
                    e.printStackTrace();
                }
                System.out.println("消费者1:"+new String(body));
                //开启手动确认消息
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}
消费者12你好,work queue


public class Consumer2 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        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);
            }
        });
    }
}
消费者21你好,work queue
消费者23你好,work queue
消费者24你好,work queue
消费者25你好,work queue
消费者26你好,work queue
消费者27你好,work queue
消费者28你好,work queue
消费者29你好,work queue
消费者210你好,work queue

广播队列

RabbitMQ实战教程笔记_第2张图片

fanout模型。

  • 可以有多个消费者。
  • 每一个消费者都有自己的queue。
  • 每个queue都要绑定到exchange。
  • 生产者发送的消息只能发送到exchange,exchange决定把消息发给哪个queue。
  • exchange会把消息发送给所有绑定的queue。
  • queue对应的消费者都能够拿到消息。实现一条消息被多个消费者消费。

生产者

public class Provider {
     

    @Test
    public void sendMessage() throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //将通道声明指定的交换机
        //第一个参数是交换机的名字,第二个参数是交换机的类型
        channel.exchangeDeclare("logs","fanout");

        /**
         * 发送消息
         * 第一个参数是交换机的名字
         * 第二个参数是路由key(fanout模型中写了也没用)
         * 第三个参数是队列中的消息进行持久化
         * 第四个参数是消息的内容
         */
        channel.basicPublish("logs","", MessageProperties.PERSISTENT_TEXT_PLAIN,"我是 fanout queue".getBytes());
        RabbitMQUtils.closeConnectionAndChannel(connection,channel);
    }
}

消费者1

每一个消费者代码都一样

public class Consumer1 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //通道绑定交换机
        channel.exchangeDeclare("logs","fanout");
        //建立临时队列,避免浪费资源
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机
        //第三个参数路由key(在fanout模型中没有用)
        channel.queueBind(queueName,"logs","");
        //消费消息
        channel.basicConsume(queueName,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:我是 fanout queue
消费者2:我是 fanout queue
消费者3:我是 fanout queue

路由队列(direct)

RabbitMQ实战教程笔记_第3张图片

Direct直连模型:

  • 队列与交换机的绑定,不再是任意绑定了,而是要指定一个RountingKey
  • 消息在向Exchange发送的时候也要指定RountingKey
  • Exchange不再是直接把消息交给每一个队列,而是根据消息的RountingKey进行判断,只有队列的RountingKey与消息的RountingKey完全一致,才会接收到消息。

生产者

public class Provider {
     

    @Test
    public void sendMessage() throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //将通道声明指定的交换机;第一个参数是交换机的名字,第二个参数是交换机的类型
        channel.exchangeDeclare("logs_direct","direct");
        //定义路由key
        String rountingKey = "info";
        /**
         * 发送消息
         * 第一个参数是交换机的名字
         * 第二个参数是路由key(fanout模型中写了也没用)
         * 第三个参数是队列中的消息进行持久化
         * 第四个参数是消息的内容
         */
        channel.basicPublish("logs_direct",rountingKey, MessageProperties.PERSISTENT_TEXT_PLAIN,("我是 direct queue,我的rountingKey是:"+rountingKey).getBytes());
        RabbitMQUtils.closeConnectionAndChannel(connection,channel);
    }
}

消费者1

public class Consumer1 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //通道绑定交换机
        channel.exchangeDeclare("logs_direct","direct");
        //建立临时队列,避免浪费资源
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机,以及路由key(可以绑定多个RountingKey)
        channel.queueBind(queueName,"logs_direct","info");
        channel.queueBind(queueName,"logs_direct","warn");
        channel.queueBind(queueName,"logs_direct","error");
        //消费消息
        channel.basicConsume(queueName,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:我是 direct queue,我的rountingKey是:info

消费者2

public class Consumer2 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //通道绑定交换机
        channel.exchangeDeclare("logs_direct","direct");
        //建立临时队列,避免浪费资源
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机,以及路由key
        channel.queueBind(queueName,"logs_direct","error");
        //消费消息
        channel.basicConsume(queueName,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));
            }
        });
    }
}

消费者2没有任何打印,说明没有收到任何消息。

路由队列(topic)

RabbitMQ实战教程笔记_第4张图片

topic实际上就比direct模型多了个通配符的功能。

  • ==*==匹配一个单词。
  • ==#==匹配多个单词。

生产者

public class Provider {
     

    @Test
    public void sendMessage() throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //将通道声明指定的交换机;第一个参数是交换机的名字,第二个参数是交换机的类型
        channel.exchangeDeclare("logs_topic","topic");
        //定义路由key
        String rountingKey = "user.save";
        /**
         * 发送消息
         * 第一个参数是交换机的名字
         * 第二个参数是路由key(fanout模型中写了也没用)
         * 第三个参数是队列中的消息进行持久化
         * 第四个参数是消息的内容
         */
        channel.basicPublish("logs_topic",rountingKey, MessageProperties.PERSISTENT_TEXT_PLAIN,("我是 topic queue,我的rountingKey是:"+rountingKey).getBytes());
        RabbitMQUtils.closeConnectionAndChannel(connection,channel);
    }
}

消费者1

public class Consumer1 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //通道绑定交换机
        channel.exchangeDeclare("logs_topic","topic");
        //建立临时队列,避免浪费资源
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机,以及路由key
        channel.queueBind(queueName,"logs_topic","user.*");
        //消费消息
        channel.basicConsume(queueName,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:我是 topic queue,我的rountingKey是:user.save

消费者2

public class Consumer2 {
     
    public static void main(String[] args) throws IOException {
     
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        //通道绑定交换机
        channel.exchangeDeclare("logs_topic","topic");
        //建立临时队列,避免浪费资源
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机,以及路由key
        channel.queueBind(queueName,"logs_topic","user.*.*");
        //消费消息
        channel.basicConsume(queueName,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));
            }
        });
    }
}

消费者2没有任何打印,说明没有收到任何消息。

Springboot整合RabbitMQ

前提配置

1、pom依赖

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
        dependency>

2、yaml文件

spring:
  application:
    name: rabbitmq-springboot
  rabbitmq:
    host: xx.xxx.xxx.xx
    port: 5672
    username: guest
    password: guest
    virtual-host: /ems    # 虚拟主机地址

simple模型

1、生产者

    /**
     * 简单模型
     */
    @Test
    public void simpleQueueSendMessage(){
     
        rabbitTemplate.convertAndSend("hello","你好,我是 hello queue");
    }

2、消费者

@Component
//消费者声明一个hello队列,以后就会监听这个队列
@RabbitListener(queuesToDeclare = @Queue(value = "hello",durable = "true",autoDelete = "false"))
public class Consumer {
     

    //消费者接收到消息后的回调函数
    @RabbitHandler
    public void receiveMessage(String message){
     
        System.out.println("message:"+message);
    }
}

你好,我是 hello queue

work模型

1、生产者

    /**
     * work模型
     */
    @Test
    public void workQueueSendMessage(){
     
        for (int i = 0; i < 10; i++) {
     
            rabbitTemplate.convertAndSend("work","你好,我是 work queue");
        }
    }

2、消费者

@Component
public class WorkConsumer {
     

    //如果想要声明多个消费者,就把@RabbitHandler换成@RabbitListener加在方法上面
    @RabbitListener(queuesToDeclare = @Queue("work"))
    public void receive01(String message){
     
        System.out.println("第一个消费者接收到的message:"+message);
    }

    @RabbitListener(queuesToDeclare = @Queue("work"))
    public void receive02(String message){
     
        System.out.println("第二个消费者接收到的message:"+message);
    }
}

第二个消费者接收到的message:你好,我是 work queue
第一个消费者接收到的message:你好,我是 work queue
第一个消费者接收到的message:你好,我是 work queue
第二个消费者接收到的message:你好,我是 work queue
第一个消费者接收到的message:你好,我是 work queue
第二个消费者接收到的message:你好,我是 work queue
第一个消费者接收到的message:你好,我是 work queue
第二个消费者接收到的message:你好,我是 work queue
第一个消费者接收到的message:你好,我是 work queue
第二个消费者接收到的message:你好,我是 work queue

fanout模型

1、生产者

    /**
     * fanout模型
     */
    @Test
    public void fanoutQueueSendMessage(){
     
        rabbitTemplate.convertAndSend("logs_fanout","","你好,我是 fanout queue");
    }

2、消费者

@Component
public class FanoutConsumer {
     

    @RabbitListener(bindings = {
     
            @QueueBinding(
                    value = @Queue,      //不指定队列名相当于是一个临时队列
                    exchange = @Exchange(value = "logs_fanout",type = "fanout")  //绑定交换机
            )
    })
    public void receive01(String message){
     
        System.out.println("第一个消费者接收到的message:"+message);
    }

    @RabbitListener(bindings = {
     
            @QueueBinding(
                    value = @Queue,      //不指定队列名相当于是一个临时队列
                    exchange = @Exchange(value = "logs_fanout",type = "fanout")  //绑定交换机
            )
    })
    public void receive02(String message){
     
        System.out.println("第二个消费者接收到的message:"+message);
    }
}

第一个消费者接收到的message:你好,我是 fanout queue
第二个消费者接收到的message:你好,我是 fanout queue

路由direct模型

1、生产者

    /**
     * 路由direct模型
     */
    @Test
    public void directQueueSendMessage(){
     
        rabbitTemplate.convertAndSend("logs_direct","info","你好,我是 路由direct queue");
    }

2、消费者

@Component
public class DirectConsumer {
     

    @RabbitListener(bindings = {
     
            @QueueBinding(
                    value = @Queue,        //不指定队列名相当于是一个临时队列
                    exchange = @Exchange(value = "logs_direct",type = "direct"),  //绑定交换机
                    key = {
     "info","warn","error"}  //设置路由key
            )
    })
    public void receive01(String message){
     
        System.out.println("第一个消费者接收到的message:"+message);
    }

    @RabbitListener(bindings = {
     
            @QueueBinding(
                    value = @Queue,        //不指定队列名相当于是一个临时队列
                    exchange = @Exchange(value = "logs_direct",type = "direct"),  //绑定交换机
                    key = {
     "error"}              //设置路由key
            )
    })
    public void receive02(String message){
     
        System.out.println("第二个消费者接收到的message:"+message);
    }
}

第一个消费者接收到的message:你好,我是 路由direct queue

路由topic模型

  • ==*==匹配一个单词。
  • ==#==匹配多个单词。

1、生产者

    /**
     * 路由direct模型
     */
    @Test
    public void directQueueSendMessage(){
     
        rabbitTemplate.convertAndSend("logs_direct","info","你好,我是 路由direct queue");
    }

2、消费者

@Component
public class TopicConsumer {
     

    @RabbitListener(bindings = {
     
            @QueueBinding(
                    value = @Queue,          //不指定队列名相当于是一个临时队列
                    exchange = @Exchange(value = "logs_topic",type = "topic"),  //绑定交换机
                    key = {
     "user.*"}  //设置路由key
            )
    })
    public void receive01(String message){
     
        System.out.println("第一个消费者接收到的message:"+message);
    }

    @RabbitListener(bindings = {
     
            @QueueBinding(
                    value = @Queue,          //不指定队列名相当于是一个临时队列
                    exchange = @Exchange(value = "logs_topic",type = "topic"),  //绑定交换机
                    key = {
     "user.insert.*"}  //设置路由key
            )
    })
    public void receive02(String message){
     
        System.out.println("第二个消费者接收到的message:"+message);
    }
}

第一个消费者接收到的message:你好,我是 路由topic queue

MQ的应用场景

1、异步处理

场景说明:用户注册后,需要发送注册邮件与注册短信,传统的做法有两种,一是串行,而是并行。

使用MQ:引入消息队列后,用户的响应时间 = 写入数据库的时间 + 写入消息队列的时间。

2、应用解耦

场景说明:双十一狂欢节,用户下单后,订单系统需要调用库存系统,传统的做法是订单系统调用库存系统的接口。这样做有很大的缺陷,假如库存系统故障,会导致用户下单失败,流失订单。

使用MQ:

  • 订单系统:用户下单后,订单系统完成持久化操作,将消息写入消息队列,返回用户下单成功;
  • 库存系统:订阅下单的消息,获取消息后进行库存操作,就算库存系统故障,消息队列也能够保证消息的可靠传递,不会导致消息丢失。

3、流量削峰

场景说明:秒杀活动,一般会因为流量过大导致应用挂掉。

使用MQ:用户的请求被服务器收到后,首先写入消息队列,如果加入消息队列数量超过一定的阈值,就直接抛弃该次请求,然后返回给用户一个抢购失败的错误页面。

RabbitMQ集群

普通集群(副本集群)

只是一个主备模式:

  • slave节点只存储exchange的信息,队列信息只在master节点,并且master节点宕机后,slave节点也不能反客为主成为新的master节点
  • slave节点同步着master中队列信息,当master启动的时候,消费者可以从slave中拿到队列消息(此刻是slave向master请示获取的)进行消费;但是当master节点宕机后,消费者是不能够访问slave节点进行消费的,必须等到master重新启动才行。
1、将每一个节点下/var/lib/rabbitmq/.erlang.cookie文件进行同步,都设置为一样的

2、给每一个节点都配置域名映射 vim /etc/hosts
172.17.0.4 mq1
172.17.0.5 mq2
172.17.0.6 mq3

3、关闭两个slave节点,执行命令加入集群,启动服务
rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@mq1
rabbirmqctl start_app

4、查看集群状态
rabbitmqctl cluster_status

你可能感兴趣的:(springboot,RabbitMQ,docker,rabbitmq,java)