1、MQ概述:
MQ
全称
Message Queue
(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。
1.1、MQ的优势:
1、应用解耦
MQ
相当于一个中介,生产方通过
MQ
与消费方交互,它将应用程序进行解耦合.
系统的耦合性越高,容错性就越低,可维护性就越低,使用 MQ
使得应用间解耦,提升容错性和可维护性。
2、任务异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
3、削峰填谷
如订单系统,在下单的时候就会往数据库写数据。但是数据库只能支撑每秒
1000
左右的并发写入,并发量再高就容易宕机。低峰期的时候并发也就100
多个,但是在高峰期时候,并发量会突然激增到
5000
以上,这个时候数据库肯定卡死了。
但是使用了
MQ
之后,限制消费消息的速度为
1000
,但是这样一来,高峰期产生的数据势必会被积压在MQ中,高峰就被
“
削
”
掉了。但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会 维持在1000QPS
,直到消费完积压的消息
,
这就叫做
“
填谷
”。
1.3、MQ的劣势
a、系统可用性降低:
系统引入的外部依赖越多,系统稳定性越差。一旦
MQ
宕机,就会对业务造成影响。如何保证
MQ
的高 可用?
b、系统复杂度提高:
MQ
的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过
MQ
进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
c、一致性问题
A
系统处理完业务,通过
MQ
给
B
、
C
、
D
三个系统发消息,如果
B
系统、
C
系统处理成功,
D
系统处理失败。如何保证消息数据处理的一致性?
1.4、RabbitMQ
RabbitMQ
基础架构如下图:
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
的分发依据
RabbitMQ
提供了
6
种模式:简单模式,
work
模式,
Publish/Subscribe
发布与订阅模式,
Routing
路由模式,Topics
主题模式,
RPC
远程调用模式(远程调用,不太算
MQ
;暂不作介绍);
1--> 简单模式:
P:生产者,也就是要发送消息的程序
C:消费者:消息的接受者,会一直等待消息到来。
queue
:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费
者从其中取出消息。
2--->工作模式:
Work Queues
与入门程序的
简单模式
相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息。
应用场景
:对于 任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
3--->订阅模式:
在订阅模型中,多了一个
exchange
角色,而且过程略有变化:
P
:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给
X
(交换机)
C
:消费者,消息的接受者,会一直等待消息到来。
Queue
:消息队列,接收消息、缓存消息。
Exchange
:交换机,图中的
X
。一方面,接收生产者发送的消息。另一方面,知道如何处理消
息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决
Exchange的类型。
Exchange
有常见以下
3
种类型:
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct
:定向,把消息交给符合指定
routing key
的队列
Topic
:通配符,把消息交给符合
routing pattern
(路由模式) 的队列
Exchange
(交换机)只负责转发消息,不具备存储消息的能力
,因此如果没有任何队列与
Exchange
绑定,或者没有符合路由规则的队列,那么消息会丢失!
发布订阅模式:
1
、每个消费者监听自己的队列。
2
、生产者将消息发给
broker
,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收 到消息
发布订阅模式与工作队列模式的区别
1
、工作队列模式不用定义交换机,而发布
/
订阅模式需要定义交换机。
2
、发布
/
订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息
(
底层使用默认交换机)
。
3
、发布
/
订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑 定到默认的交换机 。
4----->路由模式:
路由模式特点:
队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey
(路由
key
)
消息的发送方在向
Exchange
发送消息时,也必须指定消息的
RoutingKey
。
Exchange
不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key
进行判断,只有队列
的
Routingkey
与消息的
Routing key
完全一致,才会接收到消息
Exchange交换机的类型为Direct
5----->Topics通配符模式
Topic
类型与
Direct
相比,都是可以根据
RoutingKey
把消息路由到不同的队列。只不过
Topic
类型Exchange 可以让队列在绑定
Routing key
的时候
使用通配符
!
Routingkey
一般都是有一个或多个单词组成,多个单词之间以
”.”
分割,例如:
item.insert
通配符规则:
#
:匹配一个或多个词
*
:匹配不多不少恰好
1
个词
举例:
item.#
:能够匹配
item.insert.abc
或者
item.insert
item.*
:只能匹配
item.insert
6----->模式总结:
RabbitMQ
工作模式:
1
、简单模式
HelloWorld
一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)
2
、工作队列模式
Work Queue
一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)
3
、发布订阅模式
Publish/subscribe
需要设置类型为
fanout
的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列
4
、路由模式
Routing
需要设置类型为
direct
的交换机,交换机和队列进行绑定,并且指定
routing
key
,当发送消息到交换机后,交换机会根据
routing key
将消息发送到对应的队列
5
、通配符模式
Topic
需要设置类型为
topic
的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key,当发送消息到交换机后,交换机会根据
routing key
将消息发送到对应的队列
1.5、高级特性:
a、两种模式
在使用
RabbitMQ
的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。
RabbitMQ
为我们提供了两种方式用来控制消息的投递可靠性模式。
confirm
确认模式
return
退回模式
rabbitmq
整个消息投递的路径为:
producer--->rabbitmq broker--->exchange--->queue--->consumer
消息从 producer 到 exchange 则会返回一个 confirmCallback 。
消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。
我们将利用这两个
callback
控制消息的可靠性投递.
b、ack
ack
指
Acknowledge
,确认。 表示消费端收到消息后的确认方式。
有三种确认方式:
自动确认:
acknowledge="none"
手动确认:
acknowledge="manual"
根据异常情况确认:
acknowledge="auto"
,
其中自动确认是指,当消息一旦被
Consumer
接收到,则自动确认收到,并将相应
message
从
RabbitMQ
的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么 该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck()
手动签收,如果出现异常,则调用channel.basicNack()
方法,让其自动重新发送消息。
c、
死信队列
死信队列,英文缩写:
DLX
。
Dead Letter Exchange
(死信交换机),当消息成为
Dead
message
后,可以被重新发送到另一个交换机,这个交换机就是
DLX
。
消息成为死信的三种情况:
1.
队列消息长度到达限制;
2.
消费者拒接消费消息,
basicNack/basicReject,
并且不把消息重新放入原目标队
列
,requeue=false
;
3.
原队列存在消息过期设置,消息到达超时时间未被消费;
队列绑定死信交换机:
给队列设置参数:
x-dead-letter-exchange
和
x-dead-letter-routing-key
d、延迟队列:
延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
实现方式:
1.
定时器
2.
延迟队列
使用:TTL+死信队列 组合实现延迟队列的效果。