服务异步通讯
文章目录
异步通讯是相对于同步通讯而言的。
微服务之间同步调用的优点:
- 时效性较强,可以立即得到结果
微服务之间同步调用的缺点:
- 微服务的耦合度高
- 性能和吞吐能力下降
- 有额外的资源消耗
- 有级联失败问题
异步调用的常见实现是事件驱动模式
异步通信的优点:
- 服务之间的耦合度降低
- 吞吐量提升
- 故障隔离
- 流量削峰
异步通信的缺点:
- 依赖于Broker的可靠性、安全性、吞吐能力
- 架构复杂了,业务没有明显的流程线,不好追踪管理
MQ

RabbitMQ
1、安装(部署)
两种部署方式:单机部署和集群部署,在此演示单机部署
-
docker pull rabbitmq:3-management
-
docker run \
-e RABBITMQ_DEFAULT_USER=hekai \
-e RABBITMQ_DEFAULT_PASS=123456 \
--name mq \
--hostname mq1 \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3-management
-
访问RabbitMQ后台
浏览器输入,服务器IP:15672
输入用户名:hekai,密码:123456,登录即可
2、结构
RabbitMQ的结构
概念:
- channel:操作MQ的工具
- exchange:路由消息到队列中
- queue:缓存消息
- virtual host:虚拟主机,对exchange、queue等资源的分组
3、消息模型

基本消息队列的使用流程:
4、SpringAMQP
SpringAMQP的官方地址:https://spring.io/projects/spring-amqp
SpringAMQP能够简化3中消息发送和消息接收的步骤
接下来讲解5种消息模型的逻辑和实现
4.1、基本消息队列
基本消息队列:一个队列绑定一个消费者
逻辑:
实现:
1、引依赖
2、消息发送方的微服务中编写RabbitMQ的配置,方法中使用RabbitTemplate调用convertAndSend方法发送消息
3、消息接收方的微服务中编写RabbitMQ的配置, 处理消息的方法上添加@RabbitListener注解 。注解的参数是监听的队列名称,方法参数是消息
注意:消息一旦消费就会从队列中删除。RabbitMQ没有消息回溯功能(但可以通过死信队列或其他方式实现消息回溯功能)
4.2、工作消息队列
工作消息队列:一个队列绑定多个消费者,同一条消息只会被一个消费者处理 ,因为消息一旦被消费就会从队列中删除。可以提高消息处理速度,避免队列中消息堆积。
实现:
在基本消息队列的基础上,消费者再定义一个加了@RabbitListener注解的方法即可,监听同一个队列
注意:RabbitMQ默认的预取的消息数量是没有上限的,这会导致不同处理消息能力的监听器取到并消费的消息数量是一样的,降低了服务的性能,因此在消息消费方的微服务中要配置RabbitMQ预取的消息数量,设置为1,控制预取消息的上限
4.3、发布订阅模型
发布订阅模式与上述两种模式的区别在于发布订阅模式允许同一条消息发送给多个消费者消费 。实现方式就是加入了Exchange(交换机),交换机可以绑定多个队列,将同一条消息发送给绑定的多个队列中,从而实现多个消费者消费同一条消息。(队列中的消息始终只能被一个消费者消费)
在发布订阅模型中,交换机与队列有这几种绑定方式:广播、路由、话题等,分别对应着三种交换机:FanoutExchange、DirectExchange、TopicExchange
注意:Exchange只负责消息的转发,不负责消息的存储,一旦消息转发失败,则这条消息就会丢失。
4.3.1、FanoutExchange(广播类型的交换机)
FanoutExchange会将接收到的消息路由到每一个跟其绑定的queue
逻辑:
实现:
1、在消费消息的服务中,代码声明交换机和队列,并绑定
2、在消费消息的服务中,编写监听队列的方法
3、编写发送消息的方法,指定交换机名称和消息,routingKey为空字符串
4.3.2、DirectExchange(路由类型的交换机)

此外,优化一下Exchange、queue、binding的声明,上述在FanoutExchange中这些都是使用Bean声明的,比较麻烦。这里使用@RabbitListener注解声明。
逻辑:
实现:
1、使用@RabbitListener注解声明Binding、Exchange、Queue、RoutingKey,并编写消费消息的方法体
2、消息发送方的微服务中编写消息方法的方法,向交换机发送消息
DirectExchange与FanoutExchange的区别:FanoutExchange会将消息发送给与之绑定的所有的队列,而DirectExchange只会将消息发送给与之绑定的且RoutingKey和队列的BindingKey一致的队列。
4.3.3、TopicExchange(话题类型的交换机)

逻辑:
实现:
具体实现与上述的DirectExchange的实现类似,只是交换机类型、bindingkey和发送消息时指定的routingkey不同罢了
5、消息转换器
在SpringAMQP的发送方法时,消息的类型是Object,也就是说可以发送任意对象类型的消息,比如String、Map、List,自定义的Java对象等,SpringAMQP会将消息序列化为字节后再发送。
发送消息不用改,在发送消息的微服务中引入下面的依赖并声明MessageConverter的Bean即可
在接收消息的微服务中也要引入下面的依赖并声明MessageConverter的Bean;接收消息时消息的类型要与发送消息时消息的类型一致,因为要将消息反序列化 。其他代码不用改
总结: