1.1. MQ
概述
MQ
全称
Message Queue
(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统 之间进行通信。
应用之间的远程调用
![](https://img-blog.csdnimg.cn/4df00133cc0d440e85af4ae2d2aa571f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_18,color_FFFFFF,t_70,g_se,x_16)
加入
MQ
后应用之间的调用
![](https://img-blog.csdnimg.cn/087d0ea8e76049c4bbab7f25d82750f3.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_19,color_FFFFFF,t_70,g_se,x_16)
1.2.MQ
的优势:
1
、应用解耦
MQ
相当于一个中介,生产方通过
MQ
与消费方交互,它将应用程序进行解耦合。
系统的耦合性越高,容错性就越低,可维护性就越低。
![](https://img-blog.csdnimg.cn/102ddec3cbbd4710ae145357d5268467.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
使用
MQ
使得应用间解耦,提升容错性和可维护性。
![](https://img-blog.csdnimg.cn/1ce0da428fb94414a98063882670c726.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
2
、任务异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响 应时间。
![](https://img-blog.csdnimg.cn/dc8f208208dc47c58ef2ff8bcef607a2.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
一个下单操作耗时:
20 + 300 + 300 + 300 = 920ms
用户点击完下单按钮后,需要等待
920ms
才能得到下单响应,太慢!
![](https://img-blog.csdnimg.cn/c97b0bc3684744faab1a754a5ad166a0.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
用户点击完下单按钮后,只需等待
25ms
就能得到下单响应
(20 + 5 = 25ms)
。
提升用户体验和系统吞吐量(单位时间内处理请求的数目)。
3
、削峰填谷
如订单系统,在下单的时候就会往数据库写数据。但是数据库只能支撑每秒
1000
左右的并发写入,并发 量再高就容易宕机。低峰期的时候并发也就100
多个,但是在高峰期时候,并发量会突然激增到
5000
以 上,这个时候数据库肯定卡死了。
![](https://img-blog.csdnimg.cn/db29ae9afdc74290a312a8f0d894f281.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
消息被
MQ
保存起来了,然后系统就可以按照自己的消费能力来消费,比如每秒
1000
个消息,这样慢慢 写入数据库,这样就不会卡死数据库了
![](https://img-blog.csdnimg.cn/979cfb3d0ce14428b18a5429b6090ff9.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
但是使用了
MQ
之后,限制消费消息的速度为
1000
,但是这样一来,高峰期产生的数据势必会被积压在 MQ中,高峰就被
“
削
”
掉了。但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会 维持在1000QPS
,直到消费完积压的消息
,
这就叫做
“
填谷
”
![](https://img-blog.csdnimg.cn/4fbe3cab8ae6455d89b33ef592fca8a2.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
1.3. MQ
的劣势
系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦
MQ
宕机,就会对业务造成影响。如何保证
MQ
的高可用?
系统复杂度提高
MQ
的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过
MQ
进行异步调用。
如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
一致性问题
A
系统处理完业务,通过
MQ
给
B
、
C
、
D
三个系统发消息,如果
B
系统、
C
系统处理成功,
D
系统处理 失败。如何保证消息数据处理的一致性?
1.4.
常见的
MQ
产品
目前业界有很多的
MQ
产品,例如
RabbitMQ
、
RocketMQ
、
ActiveMQ
、
Kafka
、
ZeroMQ
、
MetaMq 等,也有直接使用 Redis
充当消息队列的案例,而这些消息队列产品,各有侧重,在实际选型时,需要 结合自身需求及 MQ
产品特征,综合考虑。
![](https://img-blog.csdnimg.cn/ca9d72c1c4854ad59da49ea70fbbaadb.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_18,color_FFFFFF,t_70,g_se,x_16)
1.5. AMQP
和
JMS
实现
MQ
的大致有两种主流方式:
AMQP
、
JMS
。
AMQP
AMQP
,即
Advanced Message Queuing Protocol
(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,遵循此协议,不收客户端和中间件产品和开发语言限制。2006
年,
AMQP
规范发布。类比
HTTP
。
JMS
JMS
即
Java
消息服务(
JavaMessage Service
)应用程序接口,是一个
Java
平台中关于面向消息中间件的API
JMS 是 JavaEE
规范中的一种,类比
JDBC
很多消息中间件都实现了
JMS
规范,例如:
ActiveMQ
。
RabbitMQ
官方没有提供
JMS
的实现包,但是开源社区有
AMQP
与
JMS
区别
JMS
是定义了统一的接口,来对消息操作进行统一;
AMQP
是通过规定协议来统一数据交互的格式
JMS
限定了必须使用
Java
语言;
AMQP
只是协议,不规定实现方式,因此是跨语言的。
JMS
规定了两种消息模式;而
AMQP
的消息模式更加丰富
1.6. RabbitMQ
RabbitMQ
官方地址:
http://www.rabbitmq.com/
2007
年,
Rabbit
技术公司基于
AMQP
标准开发的
RabbitMQ 1.0
发布。
RabbitMQ
采用
Erlang
语言开发。Erlang
语言专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。
RabbitMQ 基础架构如下图:
![](https://img-blog.csdnimg.cn/2886c5a94bbc4f118ce2deb05b9ad370.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
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
;暂不作介绍;
官网对应模式介绍:
https://www.rabbitmq.com/getstarted.html
2. 安装及配置RabbitMQ
这里个人采用linux安装
3. RabbitMQ
入门
详见8-RabbitMQ.pdf
4.
高级特性
4.1
消息的可靠投递
在使用
RabbitMQ
的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。
RabbitMQ
为我 们提供了两种方式用来控制消息的投递可靠性模式。
confirm
确认模式
return
退回模式
rabbitmq
整个消息投递的路径为:
producer--->rabbitmq broker--->exchange--->queue--->consumer
消息从
producer
到
exchange
则会返回一个
confirmCallback
。
消息从
exchange-->queue
投递失败则会返回一个
returnCallback
。
我们将利用这两个
callback
控制消息的可靠性投递
4.1.1
确认模式
消息从
producer
到
exchange
则会返回一个
confirmCallback
4.1.2
退回模式
消息从
exchange-->queue
投递失败则会返回一个
returnCallback
4.2 Consumer Ack
ack
指
Acknowledge
,确认。 表示消费端收到消息后的确认方式。
有三种确认方式:
自动确认:
acknowledge="none"
手动确认:
acknowledge="manual"
根据异常情况确认:
acknowledge="auto"
,(这种方式使用麻烦,不作讲解)
其中自动确认是指,当消息一旦被
Consumer
接收到,则自动确认收到,并将相应
message
从
RabbitMQ
的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck()
,手动签收,如果出现异常,则调用channel.basicNack()
方法,让其自动重新发送消息。
4.3
消费端限流
![](https://img-blog.csdnimg.cn/a938dc8fdf26444eb4a20911dbf2f7e7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
4.4 TTL
Time To Live
,消息过期时间设置
管控台中设置队列
TTL
![](https://img-blog.csdnimg.cn/33bd3baa5d584089bbe1a14de721d194.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
4.5
死信队列
死信队列,英文缩写:
DLX
。
Dead Letter Exchange
(死信交换机),当消息成为
Dead
message
后,可以被重新发送到另一个交换机,这个交换机就是
DLX
。
![](https://img-blog.csdnimg.cn/4fa4ade188474f6e874a7ca9e6221a9a.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
消息成为死信的三种情况:
1.
队列消息长度到达限制;
2.
消费者拒接消费消息,
basicNack/basicReject,
并且不把消息重新放入原目标队
列
,requeue=false
;
3.
原队列存在消息过期设置,消息到达超时时间未被消费;
队列绑定死信交换机:
给队列设置参数:
x-dead-letter-exchange
和
x-dead-letter-routing-key
![](https://img-blog.csdnimg.cn/5369ffd923bd4df3858e168372046bcd.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
4.6
延迟队列
延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
需求:
1.
下单后,
30
分钟未支付,取消订单,回滚库存。
2.
新用户注册成功
7
天后,发送短信问候。
实现方式:
1.
定时器
2.
延迟队列
![](https://img-blog.csdnimg.cn/6e075e72791f43d889c4edbfdfd03d49.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAZWhoc3Nz,size_20,color_FFFFFF,t_70,g_se,x_16)
很可惜,在RabbitMQ中并未提供延迟队列功能。但是可以使用:TTL+死信队列 组合实现延迟队列的效果
详细代码见pdf