RabbitMQ快速入门和使用

目录

消息队列的基本概述

安装RabbitMQ

编写RabbitMQ的入门程序

RabbitMQ的5种模式特征

SpringBoot整合RabbitMQ


消息队列的基本概述


  1. 消息队列是应用程序之间的通信方法;无需即时返回的且耗时的操作进行异步处理从而提高系统的吞吐量;可以实现程序之间的解耦合。
  2. 实现方式:AMQP,JMS
  3. 常见产品:activeMQ,zeroMQ,RabbitMQ,RocketMQ,kafka

安装RabbitMQ


  1. 安装erlang
  2. 安装rabbitMQ
  3. 安装RabbitMQ的图形管理界面插件
  4. 创建管理用户
  5. 创建虚拟主机Virtual Hosts

编写RabbitMQ的入门程序


入门工程

目标:

创建heima-rabbitmq的工程;用于测试RabbitMQ的消息收发。添加用于操作RabbitMQ的依赖。

搭建RabbitMQ入门工程并配置对应的maven依赖

 dependency> com.rabbitmq
        amqp-client
        5.6.0
    
        

入门工程-生产者

  1. 创建工厂(设置RabbitMQ的连接参数)
  2. 创建连接
  3. 创建频道
  4. 声明对列
  5. 发送消息
  6. 关闭资源

代码实现

在设置连接工厂的时候;如果没有指定连接的参数则会有默认值;可以去设置虚拟主机。

public class Producer {


    static final String QUEUE_NAME = "simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {

        //1. 创建工厂(设置RabbitMQ的连接参数)
        ConnectionFactory connFactory = new ConnectionFactory();
        //主机 默认:localhost
        connFactory.setHost("localhost");
        //连接端口
        connFactory.setPort(5672);
        //虚拟主机 默认/
        connFactory.setVirtualHost("/itcast");
        //用户名 默认 guest
        connFactory.setUsername("guest");
        //密码 默认 guest
        connFactory.setPassword("guest");


        //2. 创建连接
        Connection connection = connFactory.newConnection();


        //3. 创建频道
        Channel channel = connection.createChannel();

        //4. 声明对列
        /*参数
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);


        //5. 传递消息
        String mess = "hello!";
        /*
         参数1:交换机名称;如果没有则指定空字符串(表示使用默认的交换机)
         参数2:路由key,简单模式中可以使用队列名称
         参数3:消息其它属性
         参数4:消息内容
         */
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println("已发送消息:" + mess);

        //6. 关闭资源
        channel.close();
        connection.close();
    }

}

入门工程-消费者

public class Consumer  {
    public static void main(String[] args) throws Exception {
        //1. 创建连接工厂;
        //2. 创建连接;(抽取一个获取连接的工具类)
        Connection connection = ConnectionUtil.getConnection();
        //3. 创建频道;
        Channel channel = connection.createChannel();
        //4. 声明队列;
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);
        //5. 创建消费者(接收消息并处理消息);
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //接收到的消息
                System.out.println("接收到的消息为:" + new String(body, "utf-8"));
            }
        };
        //6. 监听队列
        /**
         * 参数1:队列名
         * 参数2:是否要自动确认;设置为true表示消息接收到自动向MQ回复接收到了,MQ则会将消息从队列中删除;
         * 如果设置为false则需要手动确认
         * 参数3:消费者
         */
        channel.basicConsume(Producer.QUEUE_NAME, true, defaultConsumer);

    }
}

RabbitMQ的5种模式特征


Work queues工作对列模式

模式说明

RabbitMQ快速入门和使用_第1张图片

工作队列模式:在同一个队列中可以有多个消费者,消费者之间对于消息的接收是竞争关系。


应用场景:可以在消费者端处理任务比较耗时的时候;添加对同一个队列的消费者来提高任务处理能力。

代码实现

  1. 生产者
public class Producer {
    static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws Exception {

        Connection connection = ConnectionUtil.getConnection();


        //3. 创建频道
        Channel channel = connection.createChannel();

        //4. 声明对列
        /*参数
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);


        //5. 传递消息
        for (int i = 1; i <= 30; i++) {
            String mess = "hello!" + i;
        /*
         参数1:交换机名称;如果没有则指定空字符串(表示使用默认的交换机)
         参数2:路由key,简单模式中可以使用队列名称
         参数3:消息其它属性
         参数4:消息内容
         */
            channel.basicPublish("", QUEUE_NAME, null, mess.getBytes());
            System.out.println("已发送消息:" + mess);
        }
        //6. 关闭资源
        channel.close();
        connection.close();
    }
}
  1. 消费者
public class Consumer1 {
    public static void main(String[] args) throws Exception {
        //1. 创建连接工厂;
        //2. 创建连接;(抽取一个获取连接的工具类)
        Connection connection = ConnectionUtil.getConnection();
        //3. 创建频道;
        Channel channel = connection.createChannel();
        //4. 声明队列;
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //一次只能接收并处理一个消息
        channel.basicQos(1);

        //5. 创建消费者(接收消息并处理消息);
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //接收到的消息
                System.out.println("1接收到的消息为:" + new String(body, "utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //6. 监听队列
        /**
         * 参数1:队列名
         * 参数2:是否要自动确认;设置为true表示消息接收到自动向MQ回复接收到了,MQ则会将消息从队列中删除;
         * 如果设置为false则需要手动确认
         * 参数3:消费者
         */
        channel.basicConsume(Producer.QUEUE_NAME, true, defaultConsumer);

    }
}

订阅模式类型

基本说明:

说出订阅模式中的Exchange交换机作用以及交换机的三种类型

  1. 订阅模式与前面的两种模式比较:多了一个角色Exchange交换机,接收生产者发送的消息并决定如何投递消息到其绑定的队列;消息的投递决定于交换机的类型。
  2. 交换机类型:广播(fanout)、定向(direct)、通配符(topic)

交换机只做消息转发,自身不存储数据

PS发布与订阅模式

简要说明:

发布与订阅模式特点:一个消息可以被多个消费者接收;其实是使用了订阅模式,交换机类型为:fanout广播

基本思路:

生产者

  1. 创建连接
  2. 创建频道
  3. 声明交换机(fanout)
  4. 声明队列
  5. 队列绑定到交换机
  6. 发送消息
  7. 关闭资源

消费者(至少两个消费者)

  1. 创建连接
  2. 创建频道
  3. 声明交换机
  4. 声明队列
  5. 队列绑定到交换机
  6. 创建消费者
  7. 监听队列

代码实现:

生产者:

public class Producer {


    //交换机名称
    static final String FANOUT_EXCHANGE = "fanout_exchange";
    //队列名称
    static final String FANOUT_QUEUE_1 = "fanout_queue_1";
    //队列名称
    static final String FANOUT_QUEUE_2 = "fanout_queue_2";

    public static void main(String[] args) throws Exception {
        //1. 创建连接;
        Connection connection = ConnectionUtil.getConnection();
        //2. 创建频道;
        Channel channel = connection.createChannel();
        //3. 声明交换机;参数1:交换机名称,参数2:交换机类型(fanout,direct,topic)
        channel.exchangeDeclare(FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT);
        //4. 声明队列;
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(FANOUT_QUEUE_1, true, false, false, null);
        channel.queueDeclare(FANOUT_QUEUE_2, true, false, false, null);

        //5. 队列绑定到交换机;参数1:队列名称,参数2:交换机名称,参数3:路由key
        channel.queueBind(FANOUT_QUEUE_1, FANOUT_EXCHANGE, "");
        channel.queueBind(FANOUT_QUEUE_2, FANOUT_EXCHANGE, "");

        //6. 发送消息;
        for(int i = 1; i<=10; i++) {
            String message = "你好!小兔纸。发布订阅模式 --- " + i;

            /**
             * 参数1:交换机名称;如果没有则指定空字符串(表示使用默认的交换机)
             * 参数2:路由key,简单模式中可以使用队列名称
             * 参数3:消息其它属性
             * 参数4:消息内容
             */
            channel.basicPublish(FANOUT_EXCHANGE, "", null, message.getBytes());
            System.out.println("已发送消息:" + message);
        }
        //6. 关闭资源
        channel.close();
        connection.close();
    }

}

消费者:

public class Consumer1 {
    public static void main(String[] args) throws Exception {

            //1. 创建连接;(抽取一个获取连接的工具类)
            Connection connection = ConnectionUtil.getConnection();
            //2. 创建频道;
            Channel channel = connection.createChannel();
            //3. 声明交换机
            channel.exchangeDeclare(Producer.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT);

            //4. 声明队列;
            /**
             * 参数1:队列名称
             * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
             * 参数3:是否独占本连接
             * 参数4:是否在不使用的时候队列自动删除
             * 参数5:其它参数
             */
            channel.queueDeclare(Producer.FANOUT_QUEUE_1, true, false, false, null);

            //5. 队列绑定到交换机上
            channel.queueBind(Producer.FANOUT_QUEUE_1, Producer.FANOUT_EXCHANGE, "");

            //6. 创建消费者(接收消息并处理消息);
            DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //路由key
                    System.out.println("路由key为:" + envelope.getRoutingKey());
                    //交换机
                    System.out.println("交换机为:" + envelope.getExchange());
                    //消息id
                    System.out.println("消息id为:" + envelope.getDeliveryTag());
                    //接收到的消息
                    System.out.println("消费者1 --- 接收到的消息为:" + new String(body, "utf-8"));
                }
            };
            //6. 监听队列
            /**
             * 参数1:队列名
             * 参数2:是否要自动确认;设置为true表示消息接收到自动向MQ回复接收到了,MQ则会将消息从队列中删除;
             * 如果设置为false则需要手动确认
             * 参数3:消费者
             */
            channel.basicConsume(Producer.FANOUT_QUEUE_1, true, defaultConsumer);
        }
}

Routing路由模式

路由模式特点:

  1. 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey (路由key)
  2. 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey 。
  3. Exchange不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的

Routingkey 与消息的 Routing key 完全一致,才会接收到消息

图解:

RabbitMQ快速入门和使用_第2张图片

P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。

X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列

C1:消费者,其所在队列指定了需要routing key 为 error 的消息

C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息

代码实现:

Routing 路面模式要求队列绑定到交换机的时候指定路由key;消费发送时候需要携带路由key;只有消息的路由key与队列路由key完全一致才能让该队列接收到消息。

生产方:

/**
 * 路由模式:发送消息
 */
public class Producer {
    //交换机名称
    static final String DIRECT_EXCHANGE = "direct_exchange";
    //队列名称
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    //队列名称
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";

    public static void main(String[] args) throws Exception {
        //1. 创建连接;
        Connection connection = ConnectionUtil.getConnection();
        //2. 创建频道;
        Channel channel = connection.createChannel();
        //3. 声明交换机;参数1:交换机名称,参数2:交换机类型(fanout,direct,topic)
        channel.exchangeDeclare(DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT);
        //4. 声明队列;
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT, true, false, false, null);
        channel.queueDeclare(DIRECT_QUEUE_UPDATE, true, false, false, null);

        //5. 队列绑定到交换机;参数1:队列名称,参数2:交换机名称,参数3:路由key
        channel.queueBind(DIRECT_QUEUE_INSERT, DIRECT_EXCHANGE, "insert");
        channel.queueBind(DIRECT_QUEUE_UPDATE, DIRECT_EXCHANGE, "update");

        //6. 发送消息;
        String message = "你好!小兔纸。路由模式 ;routing key 为 insert ";

        /**
         * 参数1:交换机名称;如果没有则指定空字符串(表示使用默认的交换机)
         * 参数2:路由key,简单模式中可以使用队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        channel.basicPublish(DIRECT_EXCHANGE, "insert", null, message.getBytes());
        System.out.println("已发送消息:" + message);

        message = "你好!小兔纸。路由模式 ;routing key 为 update ";

        /**
         * 参数1:交换机名称;如果没有则指定空字符串(表示使用默认的交换机)
         * 参数2:路由key,简单模式中可以使用队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        channel.basicPublish(DIRECT_EXCHANGE, "update", null, message.getBytes());
        System.out.println("已发送消息:" + message);
        //6. 关闭资源
        channel.close();
        connection.close();
    }
}

消费方:

/**
 * 路由模式;消费者接收消息
 */
public class Consumer1 {
    public static void main(String[] args) throws Exception {
        //1. 创建连接;(抽取一个获取连接的工具类)
        Connection connection = ConnectionUtil.getConnection();
        //2. 创建频道;
        Channel channel = connection.createChannel();
        //3. 声明交换机
        channel.exchangeDeclare(Producer.DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT);

        //4. 声明队列;
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列(消息会持久化保存在服务器上)
         * 参数3:是否独占本连接
         * 参数4:是否在不使用的时候队列自动删除
         * 参数5:其它参数
         */
        channel.queueDeclare(Producer.DIRECT_QUEUE_INSERT, true, false, false, null);

        //5. 队列绑定到交换机上
        channel.queueBind(Producer.DIRECT_QUEUE_INSERT, Producer.DIRECT_EXCHANGE, "insert");

        //6. 创建消费者(接收消息并处理消息);
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //接收到的消息
                System.out.println("消费者1 --- 接收到的消息为:" + new String(body, "utf-8"));
            }
        };
        //6. 监听队列
        /**
         * 参数1:队列名
         * 参数2:是否要自动确认;设置为true表示消息接收到自动向MQ回复接收到了,MQ则会将消息从队列中删除;
         * 如果设置为false则需要手动确认
         * 参数3:消费者
         */
        channel.basicConsume(Producer.DIRECT_QUEUE_INSERT, true, defaultConsumer);
    }
}

Topice通配符模式


基本说明:

  1. Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型 Exchange 可以让队列在绑定 Routing key 的时候使用通配符
  2. Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

通配符规则:

# :匹配一个或多个词

* :匹配不多不少恰好1个词

举例:

item.# :能够匹配 item.insert.abc 或者 item.insert

item.* :只能匹配 item.insert

总结

不直接Exchange交换机(默认交换机)

  1. simple简单模式:一个生产者生产一个消息到一个队列被一个消费者接收
  2. work工作队列模式:生产者发送消息到一个队列中,然后可以被多个消费者监听该队列;一个消息只能被一个消费者接收,消费者之间是竞争关系

使用Exchange交换机;订阅模式(交换机:广播fanout、定向direct、通配符topic)

  1. 发布与订阅模式:使用了fanout广播类型的交换机,可以将一个消息发送到所有绑定了该交换机的队列
  2. 路由模式:使用了direct定向类型的交换机,消费会携带路由key,交换机根据消息的路由key与队列的路由key进行对比,一致的话那么该队列可以接收到消息
  3. 通配符模式:使用了topic通配符类型的交换机,消费会携带路由key(*, #),交换机根据消息的路由key与队列的路由key进行对比,匹配的话那么该队列可以接收到消息

SpringBoot整合RabbitMQ


基本总思路:

  1. 创建springboot-rabbitmq-producer工程用于生产消息,

创建springboot-rabbitmq-consumer工程用于接收消息

  1. Spring Boot提供了对于AMQP的整合;可以使用RabbitTemplate发送消息;可以使用@RabbitListener注解接收消息。
    1. 创建工程;
    2. 添加依赖(spring-boot-stater-amqp,spring-boot-starter-test);
    3. 创建启动引导类;
    4. 添加配置文件application.yml

配置生产者工程

基本思路:

  1. 配置RabbitMQ的连接参数:主机、连接端口、虚拟主机、用户名、密码
  2. 声明交换机、队列并将队列绑定到交换机,指定的路由key(item.#)

代码实现

  1. 依赖
 
        org.springframework.boot
        spring-boot-starter-parent
        2.1.4.RELEASE
    

    com.itheima
    springboot-rabbitmq-consumer
    1.0-SNAPSHOT

    
    
        
            org.springframework.boot
            spring-boot-starter-amqp
        

    
        org.springframework.boot
        spring-boot-starter-test
    

  1. 配置RabbitMQ的连接参数:主机、连接端口、虚拟主机、用户名、密码
spring:
  rabbitmq:
    host: localhost
    port: 5672
    virtual-host: /itcast
    username: admin
    password: 123456
  1. 声明交换机、队列并将队列绑定到交换机,指定的路由key(item.#)
@Configuration
public class RabbitMQConfig  {


    //交换机名称
    public static final String ITEM_TOPIC_EXCHANGE = "item_topic_exchange";

    //队列名称
    public static final String ITEM_QUEUE = "item_queue";

    //声明交换机
    @Bean("itemTopicExchange")
    public Exchange topicExchange(){
        return ExchangeBuilder.topicExchange(ITEM_TOPIC_EXCHANGE).durable(true).build();
    }

    //声明队列
    @Bean("itemQueue")
    public Queue itemQueue(){
        return QueueBuilder.durable(ITEM_QUEUE).build();
    }

    //将队列绑定到交换机
    @Bean
    public Binding itemQueueExchange(@Qualifier("itemQueue") Queue queue,
                                     @Qualifier("itemTopicExchange")Exchange exchange){
        return BindingBuilder.bind(queue).to(exchange).with("item.#").noargs();
    }



}

配置消费者工程 :

基本思路:

  1. 配置application.yml文件,设置RabbitMQ的连接参数;
  2. 编写消息监听器接收队列(item_queue)消息;可以使用注解@RabbitListener接收队列消息

代码实现:

  1. 依赖

        org.springframework.boot
        spring-boot-starter-parent
        2.1.4.RELEASE
    

    com.itheima
    springboot-rabbitmq-consumer
    1.0-SNAPSHOT

    
        
            org.springframework.boot
            spring-boot-starter-amqp
        

    
  1. 配置application.yml文件,设置RabbitMQ的连接参数;
spring:
  rabbitmq:
    host: localhost
    port: 5672
    virtual-host: /itcast
    username: admin
    password: 123456
  1. 编写消息监听器接收队列(item_queue)消息;可以使用注解@RabbitListener接收队列消息
@Component
public class MyListener {
    
    @RabbitListener(queues = "item_queue")
    public void myListener1(String message){
        System.out.println("接收到的消息:"+message);
    }

}

测试消息发送和接收

目标:

生产者编写测试类RabbitMQTest发送消息到交换机和特定的路由(item.insert,item.update,item.delete)

基本思路:

  1. 生产者:编写测试类RabbitMQTest,利用RabbitTemplate发送3条消息,这3条消息的路由key分别是item.insert,item.update,item.delete
  2. 消费者:在IDEA控制台查看是否能接收到符合路由key的消息

代码实现:

先启动测试类进行声明交换机、队列和绑定;之后再启动消费者工程接收消息。

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void test(){
        rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_TOPIC_EXCHANGE,
                "item.insert", "商品新增,路由Key为item.insert");
        rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_TOPIC_EXCHANGE,
                "item.update", "商品新增,路由Key为item.update");
        rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_TOPIC_EXCHANGE,
                "item.delete", "商品新增,路由Key为item.delete");
        rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_TOPIC_EXCHANGE,
                "a.item.delete", "商品新增,路由Key为a.item.delete");
    }
}

你可能感兴趣的:(Java,rabbitmq,java,分布式)