java---RabbitMQ基础应用

873 阅读8分钟

RabbitMQ

1、整体概况

 1.1、概述

MQ 全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。

加入 MQ 之后的系统调用变化

 1.2、优势

  • 应用解耦

    MQ 相当于一个中介,生产方通过MQ与消费方交互,避免了两个系统之间的直接交互,从而实现系统解耦。

    系统内部的耦合性越高,关联就越强,容错性就会降低,一旦部分发生错误将会引起大量的系统功能错误,也会降低系统的维护性。

decoupling.jpg

加入中间件,子服务的错误不会影响到整体的服务提供,提高了容错性和可维护性

  • 异步处理

    将不需要进行同步处理且消耗时间较长的任务交给消息队列,由消息队列去通知消费者执行任务,主线就可以即时返回,提高系统的响应时间,提升用户体验和吞吐率。

  • 削峰填谷

    有的系统在平时的并发很低,但是在有些时间段的并发量突然会增大,如果要使系统保持一个稳定性,就需要设计满足最大量并发场景的系统,但是在平时阶段就显得很浪费资源。为了平衡高峰期的并发,就可以引入消息队列,限制消息消费的速度,这样即使突然间的大流量也会被积压在消息队列中,就降低了后续系统被冲垮的可能,这就是 削峰 。因为消息被积压在消息队列中,在高峰过后的一段时间内,消费消息的速度仍然会保持,直到消息被完全消耗,这就是 填谷

 1.3、劣势

  • 可用性降低

    系统引入的外部依赖越多,系统整体的稳定性就越低,将消息队列作为核心,一旦宕机将会对整个业务造成影响,怎样确保MQ的可用性是一个问题。

  • 复杂度提高

    以前系统间时远程的同步调用,但是现在通过MQ进行远程的异步调用,怎么保证消息的正确消费是一个问题。

  • 一致性问题

    如果消息的处理具有时序性,同样都从MQ中取出消息进行消费,怎样保证消息数据的处理一致性是一个问题。

 1.4、实现机理

  • AMQP

    AMQP ( Advanced Message Queuing Protocol )高级队列协议是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议可以使客户端和消息中间件之间传递消息,此协议不受客户端、中间件产品和开发语言的限制。

  • JMS

    JMS 即 java 消息服务(javaMessage Service)应用程序接口,是一个 Java 中关于面向消息中间件的 API,是 javaEE 规范中的一种,很多消息中间件都实现了 JMS

  • 区别

    JMSAMQP
    定义统一的接口对消息操做进行统一通过协议来统一数据交互的格式
    限定使用 Java 语言跨语言不规定实现方式
    规定两种消息模式提供更多的消息模式

 1.5、RabbitMQ

RabbitMQ官方地址:www.rabbitmq.com/

  2007年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ 1.0 发布。RabbitMQ 采用 Erlang 语言开发。Erlang 语言是专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。

structure.jpg

  • 相关概念

    名词作用
    Broker接收和分发消息的应用,RabbitMQ Server就是 Message Broker
    Virtual host多个用户使用同一个 RabbitMQ 提供的服务时,在自己的虚拟空间下进行 excahnge、queue的创建维护使用
    Connectionpublisher/consumer 和 broker 之间的 TCP 连接
    ChannelChannel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销
    Exchange根据分发规则,匹配查询表中的 routing key,分发消息到对应的 queue 中去
    Queue消息最终被送到这里等待 consumer 取走
    BindingBinding 信息被保存到 exchange 中的查询表中,用于分发 message 到 queue 中的依据
  • 分发模式

    RabbitMQ提供了 6 种模式:简单模式、work模式、发布与订阅模式、路由模式、主题模式、RPC远程调用模式。

    官网对应模式介绍:www.rabbitmq.com/getstarted.…

2、简单入门

 为了连接创建,先把连接的信息进行一个封装,后面获取连接和频道就直接进行创建即可。

/**
 * @Description	连接工具
 * @Date 2022/3/6 16:16
 * @Created 30500
 */
public class RabbitMQUtil {
    private static final String USER_NAME;
    private static final String USER_PASSWORD;
    private static final String HOST;
    private static final Integer PORT;
    private static final String VIRTUAL_HOST;

    public static Connection getConnection() throws Exception {
        //  创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //  主机地址
        connectionFactory.setHost(RabbitMQUtil.HOST);
        //  连接端口
        connectionFactory.setPort(RabbitMQUtil.PORT);
        //  虚拟主机名称
        connectionFactory.setVirtualHost(RabbitMQUtil.VIRTUAL_HOST);
        //  连接用户名
        connectionFactory.setUsername(RabbitMQUtil.USER_NAME);
        //  连接密码
        connectionFactory.setPassword(RabbitMQUtil.USER_PASSWORD);
        //  创建连接
        return connectionFactory.newConnection();
    }
}

 2.1、简单模式

 对于简单模式的模型,P 为消息的提供方,也就是生产者;C 是消息的接收方,也就是消费者,会一直等待消息的到来;中间的 红色部分 就是消息队列,生产者将消息放置在队列当中,消费者从队列当中取出消息进行消费。

  • 生产者添加消息

    中间创建会涉及到定义队列和发送消息的函数,这里先将两个函数的参数作用做一个介绍

    参数queueDeclare 创建一个队列
    String var1队列名称
    boolean var2是否定义持久化队列
    boolean var3是否独占本次连接
    boolean var4是否在不使用时自动删除队列
    Map<String, Object> var5队列其他参数
    /**
     * @Description 生产消息
     * @Date 2022/3/6 16:55
     * @Created 30500
     */
    public class Producer {
        private Connection connection;
        private Channel channel;
    
        public void producer(String message) {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.queueDeclare(RabbitModel.QUEUE_NAME_SIMPLE, true, false, false, null);
                channel.basicPublish("", RabbitModel.QUEUE_NAME_SIMPLE, null, message.getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 消费者取出消息

    消费者需要监听队列,当队列中有消息时好取出来进行消费,消费逻辑也需要单独处理。

    参数消费者监听消息 basicConsume
    String var1监听队列的名称
    boolean var2是否自动确认所受到消息回复
    Consumer var3收到消息之后的回调
    参数消息回调 handleDelivery
    String consumerTag消息者标签,在绑定监听时可定义
    Envelope envelope消息附加内容
    BasicProperties properties属性信息
    byte[] body消息主体
    /**
     * @Description 消费消息
     * @Date 2022/3/10 11:14
     * @Created 30500
     */
    public class Consumer {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.basicConsume(RabbitModel.QUEUE_NAME_SIMPLE, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                    AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

 2.2、工作模式

 工作模式和简单模式的区别是多了一个或多个消费者,同时去消费一个队列,消费者之间的关系竞争的。实现和简单模式相同,测试的时候只需要启动多个消费者即可。

  • 生产者添加消息

    public class Producer {
        private Connection connection;
        private Channel channel;
    
        public void producer(String message) {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.queueDeclare(RabbitModel.QUEUE_NAME_WORK, true, false, false, null);
                for (int i = 0; i < 10; i++) {
                    channel.basicPublish("", RabbitModel.QUEUE_NAME_WORK, null, (message + i).getBytes());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 消费者抓取消息

    public class Consumer {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.basicConsume(RabbitModel.QUEUE_NAME_WORK, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    

 2.3、订阅模式

python-three.png

 相比于前面两种模式,多了一个叫做 交换机 的组件,放置在生产者和队列之间,按照定制的规则将符合规则的消息发送给不同的队列。交换机的作用决定了他只能转发消息而不能存储消息,因此没有任何队列和交换机绑定或者没有匹配的队列的话,消息将会被丢弃。

交换机类型作用
Fanout将所有的消息交给绑定在交换机上的队列,这里不是竞争模式
Direct将消息交给符合指定 routing key 的队列,按照规则全称匹配
Topic支持通配符的 Direct
  • 生产者添加消息

    参数exchangeDeclare 绑定一个交换机
    String var1交换机名字
    BuiltinExchangeType var2交换机类型
    参数exchangeBind 交换机绑定队列
    String var1交换机名
    String var2队列名
    String var3匹配规则
    public class Producer {
        private Connection connection;
        private Channel channel;
    
        public void producer(String message) {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_FANOUT, BuiltinExchangeType.FANOUT);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_FANOUT_ONE, true, false, false, null);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_FANOUT_TWO, true, false, false, null);
                channel.exchangeBind(RabbitModel.EXCHANGE_NAME_FANOUT, RabbitModel.QUEUE_NAME_FANOUT_ONE, "");
                channel.exchangeBind(RabbitModel.EXCHANGE_NAME_FANOUT, RabbitModel.QUEUE_NAME_FANOUT_TWO, "");
                for (int i = 0; i < 10; i++) {
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_FANOUT, "", null, (message + i).getBytes());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 消费者取出消息,多个消费者从不同的队列中取消息进行消费

    public class ConsumerOne {
        private Connection connection;
      private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_FANOUT, BuiltinExchangeType.FANOUT);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_FANOUT_ONE, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_FANOUT_ONE, RabbitModel.EXCHANGE_NAME_FANOUT, "");
                channel.basicConsume(RabbitModel.QUEUE_NAME_FANOUT_ONE, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    
    public class ConsumerTwo {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_FANOUT, BuiltinExchangeType.FANOUT);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_FANOUT_TWO, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_FANOUT_TWO, RabbitModel.EXCHANGE_NAME_FANOUT, "");
                channel.basicConsume(RabbitModel.QUEUE_NAME_FANOUT_TWO, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    

 启动一个生产者进行消息的投递,两个消费者来进行消费,就会发现消费的消息是一致的,也就是交换机会往这两个队列中都进行消息的推送,之间并非是竞争关系。


 2.4、路由模式

python-four.png

 使用这种模式的交换机时,需要指定具体绑定的路由键,生产者往交换机中进行消息的投递时也必须指定消息的路由键,因为交换机会根据消息的键值将消息转发到符合路由的队列中,此种模式下的路由键必须和绑定的队列路由完全一致才会进行转发投递。

 和订阅模式唯一的区别就是交换机的模式不一样,而且指定了绑定队列的路由键其他的基本一样,记住投递消息时指定键值。

  • 生产者投递消息

    public class Producer {
        private Connection connection;
        private Channel channel;
    
        public void producer(String message) {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_DIRECT, BuiltinExchangeType.DIRECT);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_DIRECT_ONE, true, false, false, null);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_DIRECT_TWO, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_FANOUT_ONE, RabbitModel.EXCHANGE_NAME_DIRECT, "one");
                channel.queueBind(RabbitModel.QUEUE_NAME_FANOUT_TWO, RabbitModel.EXCHANGE_NAME_DIRECT, "two");
    
                for (int i = 0; i < 10; i++) {
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_DIRECT, "one", null, (message + " one " + i).getBytes());
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_DIRECT, "two", null, (message + " two " + i).getBytes());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 消费者抓取消息

    public class ConsumerOne {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_DIRECT, BuiltinExchangeType.DIRECT);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_DIRECT_ONE, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_DIRECT_ONE, RabbitModel.EXCHANGE_NAME_DIRECT, "one");
                channel.basicConsume(RabbitModel.QUEUE_NAME_DIRECT_ONE, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    
    public class ConsumerTwo {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_DIRECT, BuiltinExchangeType.DIRECT);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_DIRECT_TWO, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_DIRECT_TWO, RabbitModel.EXCHANGE_NAME_DIRECT, "two");
                channel.basicConsume(RabbitModel.QUEUE_NAME_DIRECT_TWO, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    

 2.5、通配符模式

python-five.png

 此种模式和路由模式差不多,都是可以指定具体路由匹配的,不同地方就在于可以使用正则匹配的方式,也就是不必完全按照路由进行匹配,这样就极大的提高了消息的通配性。

 让我们来认识一下通配符模式的两种通配规则(一般通过 . 来分隔单词,并指定具有实际含义的单词):

规则作用
#匹配一个或多个词
*恰好能匹配一个词
  • 生产者投递消息

    public class Producer {
        private Connection connection;
        private Channel channel;
    
        public void producer(String message) {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_TOPIC, BuiltinExchangeType.TOPIC);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_TOPIC_ONE, true, false, false, null);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_TOPIC_TWO, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_TOPIC_ONE, RabbitModel.EXCHANGE_NAME_TOPIC, "one.*");
                channel.queueBind(RabbitModel.QUEUE_NAME_TOPIC_TWO, RabbitModel.EXCHANGE_NAME_TOPIC, "two.#");
    
                for (int i = 0; i < 3; i++) {
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_TOPIC, "one.one", null, (message + " one " + i).getBytes());
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_TOPIC, "two.two", null, (message + " two " + i).getBytes());
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_TOPIC, "one.one.one", null, (message + " one one " + i).getBytes());
                    channel.basicPublish(RabbitModel.EXCHANGE_NAME_TOPIC, "two.two.two", null, (message + " two two " + i).getBytes());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 消费者抓取消息

    public class ConsumerOne {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_TOPIC, BuiltinExchangeType.TOPIC);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_TOPIC_ONE, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_TOPIC_ONE, RabbitModel.EXCHANGE_NAME_TOPIC, "one.*");
                channel.basicConsume(RabbitModel.QUEUE_NAME_TOPIC_ONE, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    
    public class ConsumerTwo {
        private Connection connection;
        private Channel channel;
    
        public void consumer() {
            try {
                connection = RabbitMQUtil.getConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(RabbitModel.EXCHANGE_NAME_TOPIC, BuiltinExchangeType.TOPIC);
                channel.queueDeclare(RabbitModel.QUEUE_NAME_TOPIC_TWO, true, false, false, null);
                channel.queueBind(RabbitModel.QUEUE_NAME_TOPIC_TWO, RabbitModel.EXCHANGE_NAME_TOPIC, "two.#");
                channel.basicConsume(RabbitModel.QUEUE_NAME_TOPIC_TWO, true, new DefaultConsumer(channel) {
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //  进行业务处理
                        System.out.println("接收到的消息为 :" + new String(body, "utf-8"));
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
        }
    }
    

3、整合 Spring

 3.1、整合依赖

 依赖没有啥特殊的,引入相关支持即可。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.7.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>2.1.8.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.7.RELEASE</version>
    </dependency>
</dependencies>

 3.2、配置信息

 想要连接远程或者本地的消息服务,需要添加对应的端口信息以及角色密码对应的虚拟主机等数据。

rabbitmq.host=ip
rabbitmq.port=port
rabbitmq.username=username
rabbitmq.password=password
rabbitmq.virtual-host=/virtual-host

 3.3、XML配置

  • 首先需要添加名称空间的约束

    xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:rabbit="http://www.springframework.org/schema/rabbit"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/rabbit
           http://www.springframework.org/schema/rabbit/spring-rabbit.xsd"
    
  • 配置基础角色(交换机、队列)

    <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath*:rabbitmq.properties"/>
    <!-- 定义连接工厂 -->
    <rabbit:connection-factory id="rabbitFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    <rabbit:admin connection-factory="rabbitFactory"/>
    <!-- 定义队列,
         默认交换机类型为 direct,
         id 为 spring 中唯一编号,
         name 为队列注册名,
         auto-declare 不存在是否自动创建 -->
    <rabbit:queue id="default_queue" name="default_queue" auto-declare="true"/>
    <!-- 定义交换机 -->
    <rabbit:direct-exchange id="default_exchange" name="default_exchange" auto-declare="true">
        <!-- 交换机绑定队列 -->
        <rabbit:bindings>
            <rabbit:binding queue="default_queue"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    <!-- 定义操作对象 -->
    <rabbit:template id="rabbitTemplate" connection-factory="rabbitFactory"/>
    

 3.4、生产者发送消息

 直接使用定义好的操作对象 rabbitTemplate 来进行消息的发送即可,重载的 convertAndSend 可支持队列、交换机、指定路由的消息发送。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class Producer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void producer() throws InterruptedException {
        rabbitTemplate.convertAndSend("default_queue",
                "hello world & beordie");
        System.out.println("发送成功");
    }
}

 3.5、消费者抓取消息

 消费者需要继承 MessageListener 实现方法来消费消息,将定义好的类交给容器去管理并绑定响应的队列即可。

<bean id="rabbirConsumer" class="com.beordie.Consumer"/>
<!-- 绑定队列 -->
<rabbit:listener-container connection-factory="rabbitFactory" auto-declare="true">
    <rabbit:listener ref="rabbirConsumer" queue-names="default_queue"/>
</rabbit:listener-container>
public class Consumer implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");
            System.out.println("接收消息: " + message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4、整合SpringBoot

 4.1、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

 4.2、配置连接信息

spring:
  profiles:
    active: dev
  rabbitmq:
    host: host
    port: port
    virtual-host: /admin
    username: username
    password: password

 4.3、声明配置

@Configuration
public class RabbitMQConfig {
    /**
     * 声明交换机
     * @return 直接路由交换机实例
     */
    @Bean("labelExchange")
    public Exchange labelExchange() {
        return ExchangeBuilder.directExchange(ExchangeName.LABEL_EXCHANGE.getExchangeName())
                .durable(true).build();
    }

    /**
     * 声明队列
     * @return 标签队列
     */
    @Bean("labelQueue")
    public Queue labelQueue() {
        return QueueBuilder.durable(QueueName.LABEL_QUEUE.getQueueName()).build();
    }

    @Bean
    public Binding labelTask(@Qualifier("labelQueue") Queue queue,
                             @Qualifier("labelExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("label").noargs();
    }
}

 4.4、监听器

@Component
public class LabelLisenter {
    @RabbitListener(queues = "labelQueue")
    public void listener(String message) {
        System.out.println("接收消息: " + message);
    }
}

 配置完成之后直接通过 rabbitmq starter 启动器进行调用,往消息队列中添加消息,如果消费者正常消费消息即成功整合。