一、MQ介绍
1. 同步调用与异步调用
微服务一旦拆分,必然涉及到服务之间的相互调用,目前我们服务之间调用采用的都是基于OpenFeign的调用。这种调用中,调用者发起请求后需要等待服务提供者执行业务返回结果后,才能继续执行后面的业务。也就是说调用者在调用过程中处于阻塞状态,因此我们成这种调用方式为同步调用,也可以叫同步通讯。但在很多场景下,我们可能需要采用异步调用的方式。
程序里所有的通信,有两种形式:同步通讯和异步通讯。
解读:
- 同步调用:就如同打视频电话,双方的交互都是实时的。因此同一时刻你只能跟一个人打视频电话。
- 异步调用:就如同发微信聊天,双方的交互不是实时的,你不需要立刻给对方回应。因此你可以多线操作,同时跟多人聊天。
两种调用方式各有优缺点:
同步调用
优点: 时效性强,等待到结果才返回
缺点: 拓展性差、性能差、级联失败
异步调用
优点:
- 耦合度低,扩展性强
- 异步调用,无需等待,性能好
- 故障隔离,下游服务故障不影响上有业务
- 缓存消息,流量削峰填谷
缺点:
- 不能立即得到调用结果,时效性差
- 不确定下游业务执行是否成功
- 业务安全依赖Broker的可靠性
- 业务复杂度增加:防止消息丢失、消息重复,要保证数据的一致性等等问题
- 系统架构复杂度增加:必须保证MQ的高可用
2. MQ介绍
Message Queue,消息对列
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
MQ的作用:服务之间数据交互时的一种方式。和Feign对比
- 异步:实现服务之间异步通信
- 削峰:MQ可以堆积消息,可以应对流量洪峰,实现流量的削峰填谷
- 解耦:实现服务之间的耦合性降低
常见的MQ有:
- ActiveMQ
- RabbitMQ
- RocketMQ
- Kafka
几种常见MQ的对比:
- | RabbitMQ | ActiveMQ | RocketMQ | Kafka |
---|---|---|---|---|
公司/社区 | Rabbit | Apache | 阿里 | Apache |
开发语言 | Erlang | Java | Java | Scala&Java |
协议支持 | AMQP,XMPP,SMTP,STOMP | OpenWire,STOMP,REST,XMPP,AMQP | 自定义协议 | 自定义协议 |
可用性 | 高 | 一般 | 高 | 高 |
单机吞吐量 | 一般 | 差 | 高 | 非常高 |
消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒以内 |
消息可靠性 | 高 | 一般 | 高 | 一般 |
- 追求可用性:Kafka、 RocketMQ 、RabbitMQ
- 追求可靠性:RabbitMQ、RocketMQ
- 追求吞吐能力:RocketMQ、Kafka
- 追求消息低延迟:RabbitMQ、Kafka
二、RabbitMQ 介绍
AMQP,Advanced Message Queuing Protocol,高级消息队列,是一种网络协议。它是应用层协议的一个开发标准,为面向消息的中间件而设计。
RabbitMQ 是一个开源的消息代理软件,也被称为消息队列系统。它支持多种消息协议,并且能够被易于地集成到各种应用程序中。
以下是 RabbitMQ 的一些关键特性和概念:
核心特性
- 多协议支持:RabbitMQ 支持多种消息协议,包括 AMQP 0-9-1、AMQP 1.0、MQTT、STOMP 等。
- 高可用性:通过集群和镜像队列支持,RabbitMQ 可以提供高可用性的消息服务。
- 灵活的路由:RabbitMQ 拥有灵活的消息路由功能,可以根据需要将消息路由到不同的队列。
- 消息持久化:支持将消息存储在磁盘上,确保消息不会因为系统崩溃而丢失。
- 确认机制:提供消息确认机制,确保消息被消费者成功处理。
- 异步处理:支持异步消息处理,提高应用程序性能。
- 扩展性:可以通过添加更多的节点来扩展 RabbitMQ 集群。
- 管理界面:提供了一个易于使用的管理界面,用于监控和管理消息队列。
基本概念
- 生产者(Producer):发送消息的应用程序或服务。
- 消费者(Consumer):接收消息的应用程序或服务。
- 队列(Queue):消息的存储地,消费者从队列中获取消息。
- 交换机(Exchange):接收生产者发送的消息,并将消息路由到一个或多个队列。
- 绑定(Binding):定义了交换机和队列之间的关系,以及如何路由消息。
- 虚拟主机(VHost):RabbitMQ 的一个命名空间,拥有自己的队列、交换机和权限设置。
- 用户权限(Permissions):控制用户对虚拟主机中资源的访问。
使用场景
- 解耦:生产者和消费者不需要直接交互,通过交换机和队列进行通信。
- 异步处理:将任务放入队列中,异步处理以提高响应性能。
- 流量削峰:在高负载时,队列可以作为缓冲,平衡系统负载。
- 任务分发:将任务分配给多个消费者进行并行处理。
三、RabbitMQ 快速入门
1.安装RabbitMQ
在线拉去镜像(Docker方式)
docker pull rabbitmq:3.8-management
2.创建MQ容器(Docker)
docker run \
-e RABBITMQ_DEFAULT_USER=XXXXXX \
-e RABBITMQ_DEFAULT_PASS=XXXXXX \
-v mq-plugins:/plugins \
--name mq \
--hostname mq \
-p 15672:15672 \
-p 5672:5672 \
-d \
--restart=always \
rabbitmq:3-management
3.RabbitMQ控制台
打开浏览器输入:http://ip:15672,账号和密码都是在创建MQ容器设定的
在控制台里,可以查看、管理:交换机、队列等等
4.SpringAMQP
1. SpringAMQP介绍
SpringAMQP是基于RabbitMQ封装的一套模板,并且还利用SpringBoot对其实现了自动装配,使用起来非常方便。
SpringAMQP 的官方地址:link
SpringAMQP提供了三个功能:
- 自动声明队列、交换机及其绑定关系
- 基于注解的监听器模式,异步接收消息
- 封装了RabbitTemplate工具,用于发送消息
2.SpringAMQP 使用示例:
- 添加依赖
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 配置RabbitMQ 连接信息
spring:
application:
name: demo-producer
rabbitmq:
host: 192.168.119.129 #RabbitMQ服务的ip
port: 5672 #RabbitMQ服务的端口
username: xxxxxx #RabbitMQ的帐号
password: xxxxxx #RabbitMQ的密码
- 创建配置类
// 创建一个配置类来配置消息的交换机(exchange)、队列(queue)和绑定(binding):
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AmqpConfig {
@Bean
public Queue queue() {
return new Queue("spring-boot-queue");
}
@Bean
public DirectExchange exchange() {
return new DirectExchange("spring-boot-exchange");
}
@Bean
public Binding binding(Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("spring-boot-routing-key");
}
}
- 创建消息生产者
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String message) {
rabbitTemplate.convertAndSend("spring-boot-exchange", "spring-boot-routing-key", message);
}
}
- 创建消息消费者
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
public class MessageConsumer {
public MessageConsumer() {
// 构造函数中可以进行初始化操作
}
@RabbitListener(queues = "spring-boot-queue")
public void receiveMessage(String message) {
System.out.println("Received message: " + message);
}
}
- 使用生产者和消费者
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAmqpApplication implements CommandLineRunner {
@Autowired
private MessageProducer messageProducer;
public static void main(String[] args) {
SpringApplication.run(SpringAmqpApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
messageProducer.sendMessage("Hello, Spring AMQP!");
}
}
3.RabbitMQ工作模式
1. 简单队列模式(Simple Queue)
在简单队列模式下,生产者发送消息到一个队列,消费者从同一个队列接收消息。这是最基本的模式,适用于点对点通信。
示例场景:用户注册后发送欢迎邮件。
- 生产者:注册服务在用户注册时发送一个消息到队列。
- 消费者:邮件服务监听队列并发送欢迎邮件。
2. 工作队列模式(Work Queues)
工作队列模式使用多个消费者来并行处理队列中的消息。当消息发送到队列时,它将被一个或多个消费者中的一个处理。
示例场景:图片处理服务。
- 生产者:上传服务在图片上传后发送一个消息到队列。
- 消费者:多个图片处理服务实例监听队列并并行处理图片。
3. 发布/订阅模式(Publish/Subscribe)
发布/订阅模式允许多个订阅者接收来自同一个队列的消息。生产者将消息发送到交换机,交换机再将消息分发到绑定的队列上。
示例场景:实时新闻更新。
- 生产者:新闻服务在新闻发布时发送消息到主题交换机。
- 订阅者:多个客户端订阅特定的新闻主题,并通过队列接收更新。
4. 路由模式(Routing)
路由模式使用交换机根据路由键将消息路由到不同的队列。这允许更细粒度的控制消息的分发。
示例场景:订单处理系统。
- 生产者:订单服务根据订单类型发送消息到交换机。
- 消费者:不同类型的订单处理服务监听不同的队列。
5. 主题模式(Topics)
主题模式是路由模式的扩展,它使用模式匹配来路由消息。这允许更复杂的路由逻辑。
示例场景:日志消息系统。
- 生产者:应用程序在不同级别生成日志消息并发送到交换机。
- 消费者:监控服务根据日志级别订阅特定的模式并处理日志。
6. RPC模式(Remote Procedure Call)
RPC 模式允许客户端通过消息队列调用远程服务,并等待响应。
示例场景:分布式计算服务。
- 客户端:发送一个计算请求到队列,并等待响应。
- 服务端:接收请求,执行计算,并将结果发送回客户端。
4.JSON消息转换器
在 RabbitMQ 中,消息转换器(Message Converters)是用于在发送和接收消息时对消息进行序列化和反序列化的组件。Spring AMQP 提供了多种消息转换器,以支持不同的数据格式和序列化机制。
消息转换器的作用:
- 序列化:在发送消息时,将对象转换为可以传输的格式(如 JSON、XML 等)。
- 反序列化:在接收消息时,将传输的数据转换回对象。
Spring AMQP 提供的消息转换器:
SimpleMessageConverter
:默认的消息转换器,适用于简单的字符串消息。Jackson2JsonMessageConverter
:使用 Jackson 库进行 JSON 序列化和反序列化。MarshallingMessageConverter
:使用 Spring 的 Marshalling 框架,可以与 JAX-B 或其他 Object Mapping 库一起使用。ByteArrayMessageConverter
:处理原始字节数组消息,不进行任何序列化或反序列化。
示例:使用 Jackson2JsonMessageConverter
假设我们有一个消息对象 Message
,我们希望在发送和接收时使用 JSON 格式。
首先,定义消息对象:
public class Message {
private String content;
// getters and setters
}
然后,配置 Jackson2JsonMessageConverter
作为自定义消息转换器:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMqConfig {
@Bean
public MessageListenerAdapter messageListenerAdapter(ObjectMapper objectMapper) {
return new MessageListenerAdapter(new MessageHandler(), objectMapper);
}
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
// 其他配置...
}
创建消息处理逻辑:
public class MessageHandler {
public void handleMessage(Message message) {
// 处理接收到的消息
System.out.println("Received message: " + message.getContent());
}
}
使用 MessageListenerAdapter
来接收消息:
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class MessageConsumer {
private final MessageListenerAdapter messageListenerAdapter;
public MessageConsumer(MessageListenerAdapter messageListenerAdapter) {
this.messageListenerAdapter = messageListenerAdapter;
}
@RabbitListener(queues = "json-message-queue")
public void receive(String message) {
// 消息将被反序列化为 Message 对象
messageListenerAdapter.onMessage(message);
}
}
在这个示例中,我们配置了 Jackson2JsonMessageConverter
作为消息转换器,并使用 MessageListenerAdapter
来将接收到的 JSON 字符串反序列化为 Message
对象。这样,当消息到达时,它将自动被转换为 Java 对象,并传递给 MessageHandler
的 handleMessage
方法进行处理。
请注意,这个示例假设你已经配置了 RabbitMQ 连接和队列。此外,你可能需要根据你的具体需求调整配置和组件。使用消息转换器可以大大提高消息处理的灵活性和可维护性。
四、总结
AMQP 是一个协议标准,定义了消息队列系统中的消息传递方式。RabbitMQ 是遵循这一标准的一个具体实现,提供了一套完整的消息队列服务。使用 RabbitMQ 和 AMQP,开发者可以构建可靠、灵活和高效的分布式消息系统。