一、同步与异步
将单体服务拆分为微服务之后,必然会涉及到微服务之间的相互调用,而两个服务之间的通讯方式又分为同步通讯与异步通讯,因此在学习Rabbit MQ之前,我们需要先了解什么是同步通信与异步通信
1.1 同步通信
生活中的同步意为两个人同时做某种动作,但是在程序开发中同步的意义恰好与生活中相反,同步通讯是A调用B时,在B将调用结果返回之前时,A都会保持等待,直到B将调用结果返回
1.1.1 优点
- 时效性强
1.1.2 缺点
- 性能低:被调用接口若迟迟没有返回,则程序会一直等待
- 耦合度高:上述例子中A高度依赖于B
- 会发生级联错误:若B抛出异常也会导致A不可用
1.2 异步通信
相反的编程中的异步则是代表两个程序同时运行,彼此之间互不影响,例如线程就是标准的异步执行的案例
1.2.1 优点
- 性能高:发起调用者并不需要等待返回值,而是可以继续执行下面的业务
- 耦合度低:上面的例子中A并不需要等待B的返回值,即使B并不返回结果,A仍然会继续执行业务代码
- 没有级联错误:相应的B的失败并不会导致A的不可用
1.2.2 缺点
- 时效性弱
二、Rabbit MQ
2.1 介绍
Rabbit MQ是解决在微服务通讯之间,某些业务场景并不需要同步通讯的一种解决方案,他可以基于多种方案实现异步通讯,保证整个服务的高性能
2.2 架构图
- VirtualHost:虚拟主机,具备隔离效果的一个空间
- exchange:交换机,用来根据指定规则进行消息的路由具体的队列
- queue:队列,具备存储消息的容器
- 消息生产者:消息生产者,用来生产与发送消息的角色
- 消息消费者:消息消费者,用来消费消息的角色
2.3 五种通信模式
以SpringBoot项目为例简单演示常用的通讯模式如何实现!!!
# 首先需要导入依赖项
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
# 配置文件配置MQ工作地址
spring:
rabbitmq:
host: # RabbitMQ布设服务器IP
port: 5672 # 端口号,默认为5672
virtual-host: # 虚拟机空间名称
username: # 登录名
password: # 登陆密码
2.3.1 简单通讯模式
// 生产者,以测试类实现
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void simpleQueueTest(){
String queueName = "simple.queue";
String message = "Hello Rabbit! First Message";
rabbitTemplate.convertAndSend(queueName, message);
}
// 消费者
@RabbitListener(queues = "simple.queue")
public void handleSimpleQueueMessage(String msg) {
log.warn("SpringRabbitListener 接收到消息: {}", msg);
}
2.3.2 工作通讯模式
// 生产者,以测试类实现
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void workQueueTest() throws InterruptedException {
String queueName = "work.queue";
for (int i = 0; i < 10; i++) {
String message = "Hello Rabbit! First Message + " + i;
rabbitTemplate.convertAndSend(queueName, message);
Thread.sleep(20);
}
}
// 消费者
// 实现了两个消费者,不同消化能力消费者的消息接收量也不同
// 测试需要在yml加入配置,使消费者不再提前预取消息
/*
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 每一个消费者同时只能处理一个消息
*/
@RabbitListener(queues = "work.queue")
public void workQueueOneMessage(String msg) {
log.warn("消费者One 接收到工作队列消息: {}", msg);
}
@RabbitListener(queues = "work.queue")
public void workQueueTwoMessage(String msg) throws InterruptedException {
log.error("消费者Two 接收到工作队列消息: {}", msg);
Thread.sleep(2000);
}
2.3.3 发布订阅模式
// 生产者,以测试类实现
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void fanoutTest(){
String exchange = "fanout.exchange";
String msg = "广播消息";
rabbitTemplate.convertAndSend(exchange, "", msg);
}
// 消费者
@RabbitListener(queues = "fanout.queue1")
public void handleFanoutQueue1Message(String msg){
log.warn("消费者One 接收到广播消息: {}", msg);
}
@RabbitListener(queues = "fanout.queue2")
public void handleFanoutQueue2Message(String msg){
log.warn("消费者Two 接收到广播消息: {}", msg);
}
2.3.4 路由模式
路由模式可以看作是有规则的发布订阅模式,消息发送方会发送消息识别码,到达交换机时,交换机会以此为依据将消息路由相对应的消息队列中再分发给消息消费者
2.3.5 Topic模式
Topic模式是可以根据
RoutingKey把消息路由到不同的队列,Topic类型Exchange可以让队列在绑定BindingKey的时候使用通配符!
通配符规则:
#:匹配一个或多个词*:匹配不多不少恰好1个词