一、初识MQ
同步通讯
微服务间基于Feign的调用就属于同步方式,存在一些问题。
同步调用的优点:
时效性较强,可以立即得到结果
同步调用的问题:
耦合度高
性能和吞吐能力下降
有额外的资源消耗
有级联失败问题
异步通讯
异步调用常见实现就是事件驱动模式
异步通信的优点:
- 耦合度低,服务解耦
- 吞吐量提升,性能提升,吞吐量提高
- 故障隔离,服务没有强依赖,不担心级联失败问题
- 流量削峰
异步通信的缺点:
- 依赖于Broker的可靠性、安全性、吞吐能力
- 架构复杂了,业务没有明显的流程线,不好追踪管理
MQ常见框架
MQ (MessageQueue),中文是消息队列,字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。
二、RabbitMQ快速入门
RabbitMQ概述和安装
RabbitMQ是基于Erlang语言开发的开源消息通信中间件,官网地址:链接: RabbitMQ
单机部署
在Centos7虚拟机中使用Docker来安装
下载镜像
方式一:在线拉取
Docker pull rabbitmq:3-management
方式二:从本地加载
评论区可单独留言跟我要取
上传虚拟机使用命令加载镜像
docker load -i mq.tar
安装MQ
执行下面命令来运行MQ容器
docker run \
-e RABBITMQ_DEFAULT_USER=itcast \
-e RABBITMQ_DEFAULT_PASS=123321 \
--name mq \
--hostname mq1 \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3-management
RabbitMQ中的几个概念:
channel:操作MQ的工具
exchange:路由消息到队列中
queue:缓存消息
virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组
常见消息模型
MQ的官方文档中给出了5个MQ的Demo示例,对应了几种不同的用法:
基本消息队列(BasicQueue)
工作消息队列(WorkQueue)
发布订阅(Publish、Subscribe),又根据交换机类型不同分为三种:
Fanout Exchange:广播
Direct Exchange:路由
Topic Exchange:主题
快速入门
官方的HelloWorld是基于最基础的消息队列模型来实现的,只包括三个角色:
publisher:消息发布者,将消息发送到队列queue
queue:消息队列,负责接受并缓存消息
consumer:订阅队列,处理队列中的消息
基本消息队列的消息发送流程:
- 建立connection
- 创建channel
- 利用channel声明队列
- 利用channel向队列发送消息
基本消息队列的消息接收流程: - 建立connection
- 创建channel
- 利用channel声明队列
- 定义consumer的消费行为handleDelivery()
- 利用channel将消费者与队列绑定
三、SpringAMQP
什么是SpringAMQP
SpringAmqp的官方地址:链接: SpringAmqp
什么是AMQP?
- 应用间消息通信的一种协议,与语言和平台无关。
SpringAMQP如何发送消息? - 引入amqp的starter依赖
- 配置RabbitMQ地址
- 利用RabbitTemplate的convertAndSend方法
案例:利用SpringAMQP实现HelloWorld中的基础消息队列功能
流程如下:
- 在父工程中引入spring-amqp的依赖
- 在publisher服务中利用RabbitTemplate发送消息到simple.queue这个队列
- 在consumer服务中编写消费逻辑,绑定simple.queue这个队列
- Basic Queue 简单队列模型
步骤1:引入AMQP依赖
因为publisher和consumer服务都需要amqp依赖,因此这里把依赖直接放到父工程mq-demo中:
<!--AMQP依赖,包含RabbitMQ--><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
步骤2:在publisher中编写测试方法,向simple.queue发送消息
1.在publisher服务中编写application.yml,添加mq连接信息:
spring: rabbitmq:
host: 192.168.150.101 # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: itcast # 用户名
password: 123321 # 密码
在publisher服务中新建一个测试类,编写测试方法:
@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSimpleQueue() {
String queueName = "simple.queue";
String message = "hello, spring amqp!";
rabbitTemplate.convertAndSend(queueName, message);
}
}
Work Queue 工作队列模型
Work queue,工作队列,可以提高消息处理速度,避免队列消息堆积
案例:模拟WorkQueue,实现一个队列绑定多个消费者
基本思路如下:
- 在publisher服务中定义测试方法,每秒产生50条消息,发送到simple.queue
- 在consumer服务中定义两个消息监听者,都监听simple.queue队列
- 消费者1每秒处理50条消息,消费者2每秒处理10条消息
发布( Publish )、订阅( Subscribe )
- 发布、订阅模型-Fanout
Fanout Exchange 会将接收到的消息广播到每一个跟其绑定的queue
发布订阅模式与之前案例的区别就是允许将同一消息发送给多个消费者。实现方式是加入了exchange(交换机)。
常见exchange类型包括:
Fanout:广播
Direct:路由
Topic:话题
注意:exchange负责消息路由,而不是存储,路由失败则消息丢失
2. 发布、订阅模型-Direct
Direct Exchange 会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式(routes)。
每一个Queue都与Exchange设置一个BindingKey
发布者发送消息时,指定消息的RoutingKey
Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
- 发布、订阅模型-Topic
TopicExchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以 . 分割。
Queue与Exchange指定BindingKey时可以使用通配符:
#:代指0个或多个单词
*:代指一个单词
消息转换器
Spring的对消息对象的处理是由org.springframework.amqp.support.converter.MessageConverter来处理的。而默认实现是SimpleMessageConverter,基于JDK的ObjectOutputStream完成序列化。
如果要修改只需要定义一个MessageConverter 类型的Bean即可。推荐用JSON方式序列化,步骤如下:
我们在publisher服务引入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
SpringAMQP中消息的序列化和反序列化是怎么实现的?
利用MessageConverter实现的,默认是JDK的序列化
注意发送方与接收方必须使用相同的MessageConverter