https://www.kuangstudy.com/zl/rabbitmq#1366709722819948546
AMQP
Rabbitmq为什么是基于channel去处理而不是链接?长连接----信道channel
- 因为 Connection 是长连接。
- 连接只有一个,通道可以有多个。
什么是AMQP
AMQP全称:Advanced Message Queuing Protocol(高级消息队列协议)。是应用层协议的一个开发标准,为面向消息的中间件设计。
AMQP生产者流转过程
AMQP消费者流转过程
RabbitMQ的核心组成部分
01、RabbitMQ的核心组成部分 *
核心概念:
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());
- 默认交换机情况下,routingKey就是队列名称,如:channel.basicPublish(“”,
Queue:队列:也成为Message Queue,消息队列,保存消息并将它们转发给消费者。
02、RabbitMQ整体架构是什么样子的?
03、RabbitMQ的运行流程
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发送消息一定有一个交换机
- 如果队列没有指定交换机会默认绑定一个交换机
各个模式页面演示
- 发送的消息,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 分类,就是:路由模式。
- 一个 队列,在一个交换机绑定,可以有多个: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
- 创建 交换器:topic_exchange,类型 选topic
- 在交换器上添加 queue1,routing key填:com.#。queue2 填:*.course.*。q3填: #.order.#。q4填:#.user.*
- #号 代表0,1个,多个。可以有多级。
- *号 代表一个。只能有一级,而且必须有一级。
- 至少一定要有一个,后来老师这样说?测试的是,只能有一级。
- com.xxx.xxx多级。 q1收到。
- 指定routing key为: com,发送消息,也可以收到。
- com.course.order q1和q2和q3收到。
- com.course.order.xxx.abc 这样q2 不在满足,只有 q1和q3
- com.course.order.user.test
- com 满足q1
- order在中间 满足 q3
- user在倒数 第二个,满足q4
course
英
/kɔːs/
n.
课程;过程;道路,航线;进程,进展;方针,总方向;行动方式,
v.
奔流,快速流动;(感情、思想)涌动;(用猎犬)追踪(兔子)
参数模式 headers
-
新建 headers-exchange
-
绑定队列:q1增加arguments为: X=1。针对代码里发送时 props字段。
- q2为 Y=2
-
发送消息,Headers参数为:X=1,q1会收到。
- Headers 同时在指定 Y=1。q1和q2都会收到。
RabbitMQ支持消息的模式
参考官网:https://www.rabbitmq.com/getstarted.html
01、RabbitMQ的模式之发布订阅模式
图解
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模式
图解
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模式
图解
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)
图解
当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢?
主要有两种模式:
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)
图解
当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢?
主要有两种模式:
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 {
}
});