RabbitMQ--基础入门

1、什么MQ?

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

2、MQ的优缺点

部分图片来源:Wanght6

优点
1、 应用解耦:提高系统容错性和可维护性
在这里插入图片描述
在这里插入图片描述

2、异步提速:提升用户体验和系统吞吐量

外卖例子
在这里插入图片描述
使用RabbirMQ
在这里插入图片描述

3、削峰填谷:提高系统稳定性

假设我们有一个应用,平时访问量是每秒100请求,我们用一台服务器即可轻松应对
在这里插入图片描述

而在高峰期,访问量瞬间翻了十倍,达到每秒1000次请求,那么单台服务器肯定无法应对,这时我们可以考虑增加到10台服务器,来分散访问压力,这样就有点浪费资源了
在这里插入图片描述
这种情况,我们就可以使用RabbitMQ来进行流量削峰

这是消息队列服务器非常典型的应用场景
在这里插入图片描述

缺点

  1. 系统可用性降低
    系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。
  2. 系统复杂度提高
    MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。

3、常见的MQ有哪些?

在这里插入图片描述

4、rabbitMQ的结构以及每个组件的作用

在这里插入图片描述

  • Broker
    接收和分发消息的应用,RabbitMQ Server就是 Message Broker

  • Virtual host
    出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等

  • Connection
    publisher/consumer 和 broker 之间的 TCP 连接(生产者和消费者之间的连接)

  • Channel
    如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销

  • Exchange
    message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)

  • Queue
    消息最终被送到这里等待 consumer 取走

  • Binding
    exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据

6、RabbitMQ的模式

RabbitMQ常用的工作模式有:简单队列模式、工作队列模式、发布订阅模式、路由模式、主题模式等五种

(1)简单模式

模型图:只包含一个生产者以及一个消费者,生产者Producer将消息发送到队列中,消费者Consumer从该队列接收消息。(单生产单消费)
在这里插入图片描述
特点:只有生产者、消费者和队列组成。

P [product]: 生产者 发生消息的
红色[queue]: 队列。 存储消息的
C [consumer]: 消费者 消费消息

  • 代码操作

1、首先创建一个springboot项目,然后把项目中的src目录删掉!!
2、创建生产者子项目

点击项目名右键新建–》Module项目–》选择Maven项目–》Next–》修改生产者项目名为product–》finish
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
同理创建消费者consumer子项目

父类中的pom.xml文件
在这里插入图片描述
3、打开虚拟机,启动rabbitMQ服务!!

生产者代码(在product项目中创建包并创建类书写代码)

public class Product {
    public static void main(String[] args)throws  Exception {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");
        //创建连接对象Connection
        Connection connection=factory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //创建队列
        /**
         * String queue, 队列的名称
         * boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
         * boolean exclusive, 是否独占 false
         * boolean autoDelete, 是否自动删除  如果长时间没有发生消息 则自动删除
         * Map<String, Object> arguments 额外参数  先给null
         */
        channel.queueDeclare("fyx01",true,false,false,null);
        //发生消息
        /**
         * String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
         * String routingKey, 路由key  如果没有交换机的绑定 使用队列的名称
         * BasicProperties props, 消息的一些额外配置 目前先不加 null
         * byte[] body 消息的内容
         */
        String msg="超人!!!";
        channel.basicPublish("","fyx01",null,msg.getBytes());
    }
}

消费者代码(在consumer项目中创建包并创建类书写代码)

public class Consumer {
    public static void main(String[] args) throws Exception{
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");
        //创建连接对象Connection
        Connection connection=factory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();

        //接受消息
        /**
         * (String queue, 队列的名称
         *  boolean autoAck, 是否自动确认
         *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
         */
        DefaultConsumer callback=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //body 接受的信息
                System.out.println("消息的内容:"+new String(body));
            }
        };
        channel.basicConsume("fyx01",true,callback);
    }
}

4、测试
在这里插入图片描述
在这里插入图片描述

(2)工作者模式

模型图:多个消费者绑定到同一个队列上,一条消息只能被一个消费者进行消费。工作队列有轮训分发和公平分发两种模式。
在这里插入图片描述

  • 特点:
  1. 一个生产者
  2. 由多个消费。
  3. 统一个队列。
  4. 这些消费者之间存在竞争关系。
  • 用处:

比如批量处理上。rabbitMQ里面积压了大量的消息。

生产者代码

public class Product {
    public static void main(String[] args)throws  Exception {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");
        //创建连接对象Connection
        Connection connection=factory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //创建队列
        /**
         * String queue, 队列的名称
         * boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
         * boolean exclusive, 是否独占 false
         * boolean autoDelete, 是否自动删除  如果长时间没有发生消息 则自动删除
         * Map<String, Object> arguments 额外参数  先给null
         */
        channel.queueDeclare("fyx01",true,false,false,null);
        //发生消息
        /**
         * String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
         * String routingKey, 路由key  如果没有交换机的绑定 使用队列的名称
         * BasicProperties props, 消息的一些额外配置 目前先不加 null
         * byte[] body 消息的内容
         */
         for (int i=0;i<10;i++){
               String msg="超人!!!";               channel.basicPublish("","fyx01",null,msg.getBytes());
            }
        //生产者关闭资源
         channel.close();
         connection.close();
    }
}

消费者01代码

public class Consumer1 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者01内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx01_direct01",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        
    }
}

消费者02代码

public class Consumer2 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者02的内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx02_direct02",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

(3)发布-订阅模式

模型图:生产者将消息发送到交换器,然后交换器绑定到多个队列,监听该队列的所有消费者消费消息。
在这里插入图片描述

  • 特点:
  1. 一个生产者
  2. 多个队列。
  3. 交换机 转发消息。

生产者代码

public class Product {
    public static void main(String[] args) {
        //创建创建连接工厂 --配置连接信息
        ConnectionFactory factory = new ConnectionFactory();
        //创建连接虚拟机中的IP
        factory.setHost("192.168.31.35");

        try {
            //创建连接对象
            Connection connection = factory.newConnection();
            //创建信道
            Channel channel  = connection.createChannel();

            //创建队列
            /**
             * String queue:队列名称(自己起名)
             * boolean durable:是否该队列化 rabbitMQ服务重启后该存放是否存在
             * boolean exclusive, 是否独占 false
             * boolean autoDelete, 是否自动删除  如果长时间没有发生消息 则自动删除
             * Map<String, Object> arguments 额外参数  先给null
             */
            channel.queueDeclare("fyx01",true,false,false,null);
            channel.queueDeclare("fyx02",true,false,false,null);

            //创建交换机
            /**
             * String exchange,交换机的名称
             * BuiltinExchangeType type, 交换机的类型
             * boolean durable:是否持久化
             */
            channel.exchangeDeclare("aaa", BuiltinExchangeType.FANOUT,true);

            channel.queueBind("fyx01","aaa","");
            channel.queueBind("fyx02","aaa","");

            //发生信息
            /**
             * (String queue, 队列的名称
             * boolean autoAck, 是否自动确认
             * Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            for (int i=0;i<10;i++){
                String msg = "xx大战蜘蛛侠!!!"+i;
                //channel.basicPublish("aaa","fyx01",null,msg.getBytes());
                channel.basicPublish("aaa", "", null, msg.getBytes());
            }
            //生产者关闭资源
            channel.close();
            connection.close();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者01代码

public class Consumer1 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者01内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx01",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者02代码

public class Consumer2 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者02的内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx02",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}
  • 测试

在这里插入图片描述

(4)路由模式

模型图:生产者将消息发送到direct交换器,它会把消息路由到那些binding key与routing key完全匹配的Queue中,这样就能实现消费者有选择性地去消费消息。
在这里插入图片描述

  • 特点
  1. 一个生产者
  2. 多个消费者
  3. 多个队列。
  4. 交换机 转发消息。
  5. routekey:路由key 只要routekey匹配的消息可以到达对应队列。

生产者代码

public class Product {
    public static void main(String[] args) {
        //创建创建连接工厂 --配置连接信息
        ConnectionFactory factory = new ConnectionFactory();
        //创建连接虚拟机中的IP
        factory.setHost("192.168.31.35");

        try {
            //创建连接对象
            Connection connection = factory.newConnection();
            //创建信道
            Channel channel  = connection.createChannel();

            //创建队列
            /**
             * String queue:队列名称(自己起名)
             * boolean durable:是否该队列化 rabbitMQ服务重启后该存放是否存在
             * boolean exclusive, 是否独占 false
             * boolean autoDelete, 是否自动删除  如果长时间没有发生消息 则自动删除
             * Map<String, Object> arguments 额外参数  先给null
             */
            channel.queueDeclare("fyx01_direct01",true,false,false,null);
            channel.queueDeclare("fyx02_direct02",true,false,false,null);
            /**
             * String exchange,交换机的名称
             * BuiltinExchangeType type, 交换机的类型
             * boolean durable:是否持久化
             */
            channel.exchangeDeclare("aaa_direct", BuiltinExchangeType.DIRECT,true);
            channel.queueBind("fyx01_direct01","aaa_direct","error");

            channel.queueBind("fyx02_direct02","aaa_direct","info");
            channel.queueBind("fyx02_direct02","aaa_direct","error");
            channel.queueBind("fyx02_direct02","aaa_direct","warning");

            //发生信息
            /**
             * (String queue, 队列的名称
             * boolean autoAck, 是否自动确认
             * Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            String a1 = "金刚吃了吗???!!!";
            channel.basicPublish("aaa_direct","warning",null,a1.getBytes());
            for (int i=0;i<10;i++){
                String msg = "金刚大战哥斯拉!!!"+i;
                channel.basicPublish("aaa_direct","error",null,msg.getBytes());
            }
            //生产者关闭资源
            channel.close();
            connection.close();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者01的代码

public class Consumer1 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者01内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx01_direct01",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者02的代码

public class Consumer2 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者02的内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx02_direct02",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}
  • 测试结果

在这里插入图片描述

(5)topic主体模式

模型图:类似于正则表达式匹配的一种模式。主要使用#、*进行匹配。
在这里插入图片描述

#1. 绑定按照通配符的模式。
     *: 统配一个单词。
     #: 统配n个单词
#例如: 
hello.orange.rabbit
lazy.orange

生产者代码

public class Product {
    public static void main(String[] args) {
        //创建创建连接工厂 --配置连接信息
        ConnectionFactory factory = new ConnectionFactory();
        //创建连接虚拟机中的IP
        factory.setHost("192.168.31.35");

        try {
            //创建连接对象
            Connection connection = factory.newConnection();
            //创建信道
            Channel channel  = connection.createChannel();

            //创建队列
            /**
             * String queue:队列名称(自己起名)
             * boolean durable:是否该队列化 rabbitMQ服务重启后该存放是否存在
             * boolean exclusive, 是否独占 false
             * boolean autoDelete, 是否自动删除  如果长时间没有发生消息 则自动删除
             * Map<String, Object> arguments 额外参数  先给null
             */
            channel.queueDeclare("fyx01_topic01",true,false,false,null);
            channel.queueDeclare("fyx02_topic02",true,false,false,null);
            /**
             * String exchange,交换机的名称
             * BuiltinExchangeType type, 交换机的类型
             * boolean durable:是否持久化
             */
            channel.exchangeDeclare("aaa_topic", BuiltinExchangeType.TOPIC,true);
            channel.queueBind("fyx01_topic01","aaa_topic","*.orange.*");
            channel.queueBind("fyx02_topic02","aaa_topic","*.*.rabbit");
            channel.queueBind("fyx02_topic02","aaa_topic","lazy.#");
            //发生信息
            /**
             * (String queue, 队列的名称
             * boolean autoAck, 是否自动确认
             * Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            for (int i=0;i<10;i++){
                String msg = "大猩猩大战美眉"+i;
                channel.basicPublish("aaa_topic","lazy.orange",null,msg.getBytes());
            }
            //生产者关闭资源
            channel.close();
            connection.close();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者01的代码

public class Consumer1 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者01内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx01_topic01",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}

消费者02的代码

public class Consumer2 {
    public static void main(String[] args) {
        //创建连接工厂 --配置连接信息
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.31.35");

        //创建连接对象Connection
        Connection connection= null;
        try {
            connection = factory.newConnection();
            //创建信道
            Channel channel = connection.createChannel();
            //接受消息
            /**
             * (String queue, 队列的名称
             *  boolean autoAck, 是否自动确认
             *  Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
             */
            DefaultConsumer callback = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("消费者02的内容:"+new String(body));
                }
            };
            channel.basicConsume("fyx02_topic02",true,callback);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}
  • 测试结果

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值