RabbitMQ
1 第一章 RabbitMQ基础部分
1.1 MQ概述
MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。
- MQ,消息队列,存储消息的中间件
- 分布式系统通信两种方式:直接远程调用 和 借助第三方 完成间接通信
- 发送方称为生产者,接收方称为消费者
1.2 MQ优势和劣势
MQ优势 | MQ劣势 |
---|---|
应用解耦 | 系统可用性降低 |
异步提速 | 系统复杂度提高 |
削峰填谷 | 一致性问题 |
-
应用解耦
系统的耦合性越高,容错性就越低,可维护性就越低。
使用 MQ 使得应用间解耦,提升容错性和可维护性。 -
异步提速
一个下单操作耗时:20 + 300 + 300 + 300 = 920ms,用户点击完下单按钮后,需要等待920ms才能得到下单响应,太慢!
用户点击完下单按钮后,只需等待25ms就能得到下单响应 (20 + 5 = 25ms)。提升用户体验和系统吞吐量。 -
削峰填谷
使用了 MQ 之后,限制消费消息的速度为1000,这样一来,高峰期产生的数据势必会被积压在 MQ 中,高峰就被“削”掉了,但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000,直到消费完积压的消息,这就叫做“填谷”。使用MQ后,可以提高系统稳定性。 -
MQ的劣势
(1)系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。如何保证MQ的高可用?
(2)系统复杂度提高
MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
(3)一致性问题
A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败。如何保证消息数据处理的一致性?
1.3 常见的MQ产品
1.4 RabbitMQ 简介
AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP 规范发布。类比HTTP。
2007年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ 1.0 发布。RabbitMQ 采用 Erlang 语言开发。Erlang 语言由 Ericson 设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。
RabbitMQ 基础架构如下图:
RabbitMq中相关概念
- Broker中间件
接收和分发消息的应用,Rabbit Server就是Broker Server - 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 的分发依据 - 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 的开销 - 工作模式
RabbitMq提供了6中工作模式:
简单模式、工作队列(work queues)、发布订阅模式(Publish/Subscribe)、路由模式(Routing)、主题模式(Topics)、RPC 远程调用模式(远程调用,不太算 MQ;暂不作介绍) - JMS
JMS 即 Java 消息服务(JavaMessage Service)应用程序接口,是一个 Java 平台中关于面向消息中间件的API,JMS 是 JavaEE 规范中的一种,类比JDBC。
小结
- RabbitMQ 是基于 AMQP 协议使用 Erlang 语言开发的一款消息队列产品。
- RabbitMQ提供了6种工作模式,我们学习5种。这是今天的重点。
- AMQP 是协议,类比HTTP。
- JMS 是 API 规范接口,类比 JDBC。
1.5 RabbitMQ 的工作模式
1.5.1 简单模式
P:生产者,也就是要发送消息的程序
C:消费者,消息的接受者, 会一直等等待消息的到来
Queue:消息队列,图中红色部分,类似一个邮箱,可以缓存信息;生产者向其中投递信息,消费者从其中取出信息。
1.5.2 工作队列模式
WorkQueues:工作队列模式,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息,多个消费者之间是竞争的关系,即C1消费了C2就不会再去消费了,因为消息已经被其他消费者消费了。
应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
1.5.3 发布订阅模式
- 交换机需要与队列进行绑定,绑定之后;一个消息可以被多个消费者都收到。
- 发布订阅模式与工作队列模式的区别:
(1)工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机
(2)发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机)
(3)发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑 定到默认的交换机
1.5.4 Routing 路由模式
Routing 模式要求队列在绑定交换机时要指定 routing key,消息会转发到符合 routing key 的队列。
1.5.5 Topics 通配符模式
Topic 主题模式可以实现 Pub/Sub 发布与订阅模式和 Routing 路由模式的功能,只是 Topic 在配置routing key 的时候可以使用通配符,显得更加灵活。
1.5.6 工作模式总结
2 第二章 RabbitMQ高级特性
2.1 消息的可靠投递
在使用RabbitMq的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败。RabbitMq为我们提供了两种方式用来控制消息投递可靠性模式。RabbitMq整个消息投递的路径为producer–>rabbitmq broker–>exchange–>queue–>consumer.
- confim 确认模式
消息从 producer 到 exchange 则会返回一个 confirmCallback 。
(1)设置ConnectionFactory的publisher-confirms=“true” 开启 确认模式。
(2)rabbitTemplate.setConfirmCallback设置回调函数。当消息发送到exchange后回调confirm方法。在方法中判断ack,如果为true,则发送成功,如果为false,则发送失败,需要处理。
在生产端编写代码检测:
/**
* 确认模式:
* 1. 确认模式开启:ConnectionFactory中开启publisher-confirms="true"
* 2. 在rabbitTemplate定义ConfirmCallBack回调函数
*/
@Test
public void testConfirm() {
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {//2. 定义回调
/**
* @param correlationData 相关配置信息
* @param ack exchange交换机 是否成功收到了消息。true 成功,false代表失败
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("confirm方法被执行了....");
if (ack) {
//接收成功
System.out.println("接收成功消息" + cause);
} else {
//接收失败
System.out.println("接收失败消息" + cause);
//做一些处理,让消息再次发送。
}
}
});
//3. 发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm11", "confirm", "message confirm....");
}
在消费端手动确认:
@Component
public class AckListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//1.接收转换消息
System.out.println(new String(message.getBody()));
//2. 处理业务逻辑
System.out.println("处理业务逻辑...");
int i = 3/0;//出现错误
//3. 手动签收
channel.basicAck(deliveryTag,true);
System.out.println("签收成功:" + message.getBody());
} catch (Exception e) {
//e.printStackTrace();
//4.拒绝签收
/*
第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
*/
channel.basicNack(deliveryTag,true,true);
//channel.basicReject(deliveryTag,true);
}
}
}
- return 回退模式
消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。
在生产端编写代码:
/**
* 回退模式: 当消息发送给Exchange后,Exchange路由到Queue失败是 才会执行 ReturnCallBack
* 步骤:
* 1. 开启回退模式:publisher-returns="true"
* 2. 设置ReturnCallBack
* 3. 设置Exchange处理消息的模式:
* 1. 如果消息没有路由到Queue,则丢弃消息(默认)
* 2. 如果消息没有路由到Queue,返回给消息发送方ReturnCallBack
*/
@Test
public void testReturn() {
//设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true);
//2.设置ReturnCallBack
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
* @param message 消息对象
* @param replyCode 错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("return 执行了....");
System.out.println(message);
System.out.println(replyCode);
System.out.println(replyText);
System.out.println(exchange);
System.out.println(routingKey);
//处理
}
});
//3. 发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm", "confirm", "message confirm....111");
}
2.2 消费端限流
(1)在rabbit:listener-container 中配置 prefetch属性设置消费端一次拉取多少消息
(2)消费端的确认模式一定为手动确认。acknowledge=“manual”
2.3 TTL
2.4 死信队列
2.5 延迟队列
2.6 RabbitMQ 应用问题
- 消息可靠性保障
消息补偿机制,100%确保消息发送成功
- 消息幂等性保障
乐观锁解决方案