【学相伴飞哥】RabbitMQ 2021-03-14 笔记2:AMQP核心组成,8大模式2种实现:发布订阅,路由,主题。Work模式轮询和公平分发。

https://www.kuangstudy.com/zl/rabbitmq#1366709722819948546

AMQP

Rabbitmq为什么是基于channel去处理而不是链接?长连接----信道channel

  • 因为 Connection 是长连接。
  • 连接只有一个,通道可以有多个。

什么是AMQP

AMQP全称:Advanced Message Queuing Protocol(高级消息队列协议)。是应用层协议的一个开发标准,为面向消息的中间件设计。

AMQP生产者流转过程

img

AMQP消费者流转过程

img

RabbitMQ的核心组成部分

01、RabbitMQ的核心组成部分 *

img

核心概念:
Server:又称Broker ,接受客户端的连接,实现AMQP实体服务。 安装rabbitmq-server
Connection:连接,应用程序与Broker的网络连接 TCP/IP/ 三次握手和四次挥手
Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,客户端可以建立对各Channel,每个Channel代表一个会话任务。

  • 就好像 电线里面的铜丝。

Message :消息:服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级,延迟等高级特性,Body则就是消息体的内容。
Virtual Host 虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange

  • 就好像 CDEF盘,一样。做隔离。类似msyql的表。

Exchange:交换机,接受消息,根据路由键发送消息到绑定的队列。(不具备消息存储的能力)

  • 所有的消息,都是通过交换机(会有默认的),投递到队列。
  • 默认的为:AMQP default

Bindings:Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key.

  • 就是 Routing key 和 Exchange 绑定的一个对象。维持他们的关系。

Routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息。

  • 就是一个条件。比如:只有 user 表的 消费者,才能收到。
  • 如果 不加 Routing key 都会收到。
  • RoutingKey(路由键):用于将消息从交换机路由到某个队列
    • 默认交换机情况下,routingKey就是队列名称,如:channel.basicPublish(“”, QUEUE_NAME, null, message.getBytes());

Queue:队列:也成为Message Queue,消息队列,保存消息并将它们转发给消费者。

02、RabbitMQ整体架构是什么样子的?

img

03、RabbitMQ的运行流程

img

03、RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html

03-1、简单模式 Simple

  • 参考第12章节

03-2、工作模式 Work Queues

  • web操作查看视频
  • 类型:无
  • 特点:分发机制

03-3、发布订阅模式 Publish/Subscribe

  • web操作查看视频
  • 类型:fanout
  • 特点:Fanout—发布与订阅模式,是一种广播机制,它是没有路由key的模式。

03-4、路由模式 Routing

  • web操作查看视频
  • 类型:direct
  • 特点:有routing-key的匹配模式

03-5、主题Topic模式 Topics

  • web操作查看视频
  • 类型:topic
  • 特点:模糊的routing-key的匹配模式

03-6、参数模式

  • web操作查看视频
  • 类型:headers
  • 特点:参数匹配模式

03-7、RPC和 Publisher Confirms

小结

  • rabbitmq发送消息一定有一个交换机
  • 如果队列没有指定交换机会默认绑定一个交换机
    img

img

各个模式页面演示

  • 发送的消息,persistent 持久化 立马存盘 和 Non-persistent 重启服务消失。
  • purge Messages 是清空队列
  • 一个队列,可以绑定到多个 交换机上。交换机的名字,并不是routing key
purge
英
/pɜːdʒ/
v.
清洗(组织中的异己分子);清除,排除(不愉快的情况或感觉);删除(不需要的东西)催吐
n.
(对异己的)清洗,排除;泻药

交换机 type 的4种模式:

  • direct 径直的。路由模式。
  • fanout 分列。发布订阅
  • headers 头信息
  • topic 主题
persistent
英
/pəˈsɪstənt/
adj.
执意的,坚持不懈的;持续的,反复出现的;(化学品,放射性)作用持久的,挥发慢的;(动植物某部位,如角、叶等)宿存的,不落的

purge
英
/pɜːdʒ/
v.
清洗(组织中的异己分子);清除,排除(不愉快的情况或感觉)
n.
(对异己的)清洗,排除;泻药

durable
英
/ˈdjʊərəb(ə)l/
adj.
持久的,耐用的;<非正式>(人)有耐力的,坚韧不拔的
n.
耐用品

fanout
英
/fænaʊt/
n.
扇出;展开;分列(账户)
简单

1.简单模式 Simple

  • 选择交换机,输入 Routing key就是队列的名字。即可测试发送消息。
    • 所有的消息都是 通过 交换机 投递的。
  • 默认交换机 (AMQP default),的 类型,是 direct 。
    • 此时发送,必须指定 routing key(默认队列名)
    • 测试的为:不指定 发送失败。
        //创建队列,加入到默认交换器
		channel.queueDeclare("queue10", false, false, false, null);
		// ""发送到默认交换器,此时要指定 routingKey 默认为 队列名
        channel.basicPublish("", "queue10", null, "我的队列11110".getBytes());
		//即:生产者是 和 交换器 交互的。
		//接收队列的消费者,是 直接指定 队列名字的。
发布订阅 fanout

3.发布订阅模式 Publish/Subscribe

  • fanout 是发布订阅模式,指定 Routing Key 没意义。

    • 还是 群发的。只要 在这个交换机的 队列都会收到。
  • 选择 fanout 模式,创建fanout-exchange。绑定多个 队列(进入队列 或 进入交换机都能绑定)

  • 选择这个 交换机,发送消息(不指定Routing key),每个队列都会收到。

    • 就好像:大家都在听一个直播间讲课。都能收到消息。
路由 direct 最常用

4.路由模式 Routing

  • 路由key 全部订阅成 一样的,也是 发布订阅者模式 (没意义,多此一举),能用发布订阅,就用。

  • 得出结论:路由模式direct 就是 发布订阅模式fanout,增加了 Routing Key

    • 比如 注册用户,只需要微信 和 邮件 收到消息。不希望其他收到。
      • 相当于 指定了一个 where条件。如条件为 email
  • 队列 可以绑定了 多个交换器。如绑定:fanout-exchange和direct-exchange\

    • 一个 队列,在一个交换机绑定,可以有多个:routing key
      • 如下:queue3为 weixin 和 email
    • 根据 routing key 分类,就是:路由模式。
  • 1 创建 direct 模式的交换机。direct_exchange

  • 2 在交换机上 绑定 queue1,记得 填routing-key 如:email

    • queue2 routing-key 为:sms 短信。queue3为 weixin

    • queue3 的 routing key 叫了微信,在绑定 email

    • 现在有 queue1 和 queue3 绑定email的 routing key

  • 4 发送消息,指定 routing key为:email,queue1 和 3 会收到。

主题模式 topic
  • 发布订阅模式fanout,增加了 Routing Key:是路由模式direct
  • 主题模式,再次叠加,支持 模糊匹配的 Routing Key
  1. 创建 交换器:topic_exchange,类型 选topic
  2. 在交换器上添加 queue1,routing key填:com.#。queue2 填:*.course.*。q3填: #.order.#。q4填:#.user.*
    1. #号 代表0,1个,多个。可以有多级。
    2. *号 代表一个。只能有一级,而且必须有一级。
      1. 至少一定要有一个,后来老师这样说?测试的是,只能有一级。
  3. com.xxx.xxx多级。 q1收到。
    1. 指定routing key为: com,发送消息,也可以收到。
  4. com.course.order q1和q2和q3收到。
  5. com.course.order.xxx.abc 这样q2 不在满足,只有 q1和q3
  6. com.course.order.user.test
    1. com 满足q1
    2. order在中间 满足 q3
    3. user在倒数 第二个,满足q4
course
英
/kɔːs/
n.
课程;过程;道路,航线;进程,进展;方针,总方向;行动方式,
v.
奔流,快速流动;(感情、思想)涌动;(用猎犬)追踪(兔子)
参数模式 headers
  1. 新建 headers-exchange

  2. 绑定队列:q1增加arguments为: X=1。针对代码里发送时 props字段。

    1. q2为 Y=2
  3. 发送消息,Headers参数为:X=1,q1会收到。

    1. Headers 同时在指定 Y=1。q1和q2都会收到。

RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html

01、RabbitMQ的模式之发布订阅模式

图解

img

01-1、发布订阅模式具体实现

  • web操作查看视频
  • 类型:fanout
  • 特点:Fanout—发布与订阅模式,是一种广播机制,它是没有路由key的模式。
生产者
  // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("47.104.141.27");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 6: 准备发送消息的内容
            String message = "你好,学相伴!!!";
            //队列名字,已经用 页面创建
            String  exchangeName = "fanout-exchange";
            //发布订阅者,不需要 routingKey
            String routingKey = "";
            // 7: 发送消息给中间件rabbitmq-server
            // @params1: 交换机exchange
            // @params2: 队列名称/routingkey
            // @params3: 属性配置
            // @params4: 发送消息的内容
            
            channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
            
            System.out.println("消息发送成功!");
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}
消费者
public class Consumer {
    private static Runnable runnable = () -> {
        // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("47.104.141.27");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        //获取队列的名称。使用线程的名字
        final String queueName = Thread.currentThread().getName();
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 5: 申明队列queue存储消息
            /*
             *  如果队列不存在,则会创建
             *  Rabbitmq不允许创建两个相同的队列名称,否则会报错。
             *
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             * */
            // 这里如果queue已经被创建过一次了,可以不需要定义
            //channel.queueDeclare("queue1", false, false, false, null);
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            finalChannel.basicConsume(queueName, true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
            System.out.println(queueName + ":开始接受消息");
            System.in.read();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    };
    public static void main(String[] args) {
        // 启动三个线程去执行
        new Thread(runnable, "queue1").start();
        new Thread(runnable, "queue2").start();
        new Thread(runnable, "queue3").start();
    }

RabbitMQ入门案例 - Direct模式

RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html

01、RabbitMQ的模式之Direct模式

图解

img

01-1、direct模式具体实现

  • web操作查看视频
  • 类型:direct
  • 特点:Direct模式是fanout模式上的一种叠加,增加了路由RoutingKey的模式。
生产者
  • 如 上面小结的 路由 direct 已经设置了。q1 和 q3的 routing key为 email,现在同时给 它们发消息。
        channel.basicPublish("direct_exchange", "email", null, "路由 direct模式,发送到1,3队列".getBytes());

RabbitMQ入门案例 - Topic模式

RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html

01、RabbitMQ的模式之Topic模式

图解

img

01-1、topic模式具体实现

  • web操作查看视频
  • 类型:topic
  • 特点:Topic模式是direct模式上的一种叠加,增加了模糊路由RoutingKey的模式。
生产者
        //q1  com.#。
        //q2  *.course.*。
        //q3  #.order.#。
        //q4  #.user.*
        //q1满足,q3满足,q4满足
        String routingKey="com.order.user.test";
        channel.basicPublish("topic_exchange", routingKey, null, "我的发送内容测试啦啦啦".getBytes());

*代码创 交换机和绑定 Direct路由

  • 如果 发送消息,交换机不存在报错。
  • 如果 消费者监听的 队列不存在,报错。
        //交换机
        String exchangeName = "direct_message_exchange";
        //交换机的类型direct/topic/fanout/headers
        String exchangeType = "direct";

		//1. 创建交换器
        //持久化:所谓的持久化就是指,交换机会不会随着服务器重启造成丢失,如果是true代表不丢失,false重启就会丢失
        channel.exchangeDeclare(exchangeName, exchangeType, true);

		//2. 创建队列
        //队列名,持久化,不排他,不自动删除,没参数
        channel.queueDeclare("queue5", true, false, false, null);
        channel.queueDeclare("queue6", true, false, false, null);
        channel.queueDeclare("queue7", true, false, false, null);

		//3. 队列 和 交换器 绑定,并且指定 Routing Key
        channel.queueBind("queue5", exchangeName, "order");
        channel.queueBind("queue6", exchangeName, "order");
        channel.queueBind("queue7", exchangeName, "course");

		//给指定的 交换机 和 Routing Key 发消息。此时:队列5和6,会收到消息。
        channel.basicPublish(exchangeName, "order", null, "我的发送内容测试啦啦啦".getBytes());

abbitMQ入门案例 - Work模式 - 轮询模式(Round-Robin)

01、RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html

02、Work模式轮询模式(Round-Robin)

图解

img

当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢?
主要有两种模式:
1、轮询模式的分发:一个消费者一条,按均分配;
**2、公平分发:**根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;

Work模式 - 轮询模式(Round-Robin)

  • 类型:无
  • 特点:该模式接收消息是当有多个消费者接入时,消息的分配模式是一个消费者分配一条,直至消息消费完成;
生产者 *
  • 多个消费者,默认的模式。必须自动ack

    • A服务器 消费的快,13579很快消费完毕。B消费的慢:246810 慢慢消费。
  • work1和work2的消息处理能力不同,但是最后处理的消息条数相同,是“按均分配”。

  • 创建一个队列,发送20个消息

            channel.queueDeclare("queue10", false, false, false, null);
            // 6: 准备发送消息的内容
            for (int i = 1; i <= 20; i++) {
                //消息的内容
                String msg = "学相伴:" + i;
                // 7: 发送消息给中间件rabbitmq-server
                // @params1: 交换机exchange
                // @params2: 队列名称/routingkey
                // @params3: 属性配置
                // @params4: 发送消息的内容
                channel.basicPublish("", "queue10", null, msg.getBytes());
            }
消费者 - Work1 和 2
  • 两个消费者,都监听这个队列。
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            //finalChannel.basicQos(1);
            finalChannel.basicConsume("queue10", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    try{
                        System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(2000);
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });

RabbitMQ入门案例 - Work模式 - 公平分发(Fair Dispatch)

01、RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html

02、Work模式 - 公平分发(Fair Dispatch)

图解

img

当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢?
主要有两种模式:
1、轮询模式的分发:一个消费者一条,按均分配;
2、公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;

Work模式 - 公平分发(Fair Dispatch)

  • 类型:无
  • 特点:由于消息接收者处理消息的能力不同,存在处理快慢的问题,我们就需要能者多劳,处理快的多处理,处理慢的少处理;

消费者1 和 2 和 Qos *

  • 公平分发,一定是手动应答。如果手动应该,无效果。
  • 打开qos
    • QoS(Quality of Service,服务质量)指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力,
  • 睡眠不同的时间 模拟。
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;

			//默认的是轮询分发。qos默认为null。消费者每次从 服务器取多少个数据过来。
			//实际:要看磁盘和内存使用情况,先放10条,在情况,在增加。
			//在Overview看,内存和磁盘 转换的极限值。
			//比如内存大小设置1千M,看会不会撑爆内存。qos设1000/服务器数量,内存正常,就是合理的。不建议设置大。设置大,持久化会慢。
			//最大内存是 物理系统的 0.4倍。如物理8G,就是3.2G
			//然后看磁盘,磁盘还剩50M,会预警(无法在接收消息)。

            finalChannel.basicQos(1);
            finalChannel.basicConsume("queue1", false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    try{
                        System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(2000); //这里睡了2秒。下一个消费者睡200毫秒即可。
                        //第二个参数:false,就是单条消费
                        finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值