文章目录
- MQ知识介绍
- 同步和异步:
- MQ介绍
- MQ分类
- MQ原理
- 发布/订阅模式
- 消息传递模式
- SpringAMQP
- SpringAMQP基础使用
- 实践案例
- 简单代码示例
- WorkQueue
- 介绍
- 消费预取机制
- exchange交换机
- 介绍
- FanoutExchange
- DirectExchange
- TopicExchange
- Message转换器
- TopicExchange
- Message转换器
MQ知识介绍
同步和异步:
同步和异步是两种不同的通信方式,它们在数据传输过程中有各自的优势和劣势。
同步通信是指发送方和接收方在固定的时间间隔内进行数据传输。在同步通信中,发送方和接收方的时钟信号需要保持一致,以确保数据的准确传输。同步通信的优点包括实时性和可靠性,因为接收方可以立即得到发送方的数据,并且由于时钟信号的一致性,数据传输的错误率和丢失率较低。然而,同步通信也有一些缺点,例如较高的耦合度和资源消耗。由于需要等待对方的回复,因此发送方和接收方的处理过程紧密耦合在一起,这可能对性能和吞吐能力产生一定的影响。此外,同步通信也需要消耗额外的资源,例如等待时间和处理时间。
异步通信是指发送方可以在任何时间点开始数据传输,而接收方则需要在适当的时间点接收数据。在异步通信中,发送方不需要等待接收方的回复,因此可以随时发送数据。异步通信的优点是灵活性较高,因为发送方可以随时发送数据,而不需要等待接收方的处理结果。此外,由于不需要等待对方的回复,因此异步通信也可以降低耦合度和资源消耗。然而,异步通信也有一些缺点,例如可能会造成数据的丢失或重复传输。
综上所述,同步和异步通信都有各自的优缺点,选择哪种通信方式取决于具体的应用场景和需求。在需要实时性和可靠性的场景中,通常使用同步通信;而在需要灵活性和降低资源消耗的场景中,则通常使用异步通信。
MQ介绍
在当今的软件开发领域,微服务架构和MQ(消息队列)已成为核心组成部分。Java微服务为开发人员提供了轻量级、可扩展的解决方案,而MQ则帮助我们在分布式系统中实现异步通信和数据流动。本学习笔记将带你深入了解Java微服务和MQ的相关知识。
MQ分类
在Java微服务中,常见的MQ包括以下几种:
-
RabbitMQ:开源消息代理软件,支持多种消息队列协议,如AMQP、STOMP和MQTT。它提供了丰富的客户端库,方便与Java微服务集成。
-
ZeroMQ:高性能的异步消息库,适用于构建分布式和并发应用程序。它支持多种语言,包括Java。
-
Apache Kafka:分布式流处理平台,广泛应用于实时数据流处理场景。Kafka与Java微服务结合使用可以实现高吞吐量、可扩展的分布式消息传递。
-
ActiveMQ:开源消息代理,支持多种协议,如AMQP、STOMP和MQTT。ActiveMQ提供了灵活的配置选项和强大的API,可轻松地与Java微服务集成。
MQ原理
发布/订阅模式
发布/订阅模式是MQ的基本原理之一。在此模式下,生产者(发布者)将消息发布到一个或多个主题(Topic),而消费者(订阅者)则订阅感兴趣的主题并接收消息。这种模式实现了消息的解耦和灵活性,允许消费者按需接收消息。
消息传递模式
消息传递模式描述了如何在生产者和消费者之间传递消息。以下是一些常见的消息传递模式:
- 点对点(Point-to-Point):生产者将消息发送给一个消费者,待消费者处理完后返回确认。这种模式下,生产者等待消费者的确认,确保消息被成功处理。
- 发布/订阅(Pub/Sub):生产者将消息发布到一个或多个主题,多个消费者可以订阅这些主题并接收消息。这种模式下,消费者之间解耦,生产者和消费者之间也解耦。
- 路由(Routing):生产者将消息发送给一个中间代理(Broker),由Broker根据路由规则将消息路由给一个或多个消费者。这种模式下,生产者和消费者之间解耦,但消费者之间可能存在竞争条件。
- 队列(Queue):生产者将消息发送到一个队列,由一个消费者从队列中取出并处理消息。这种模式下,实现了生产者和消费者之间的解耦,但只有一个消费者可以处理该队列的消息。
SpringAMQP
在Java微服务中集成MQ需要考虑以下几个方面:
- 选择合适的MQ:根据项目需求选择合适的MQ,如RabbitMQ、ZeroMQ、Apache Kafka或ActiveMQ等。选择时需考虑性能、可靠性、易用性等因素。
- 引入依赖:在Java项目中引入所选MQ的依赖库或SDK,以便在代码中使用相关API。
- 定义消息格式:设计并定义消息的格式和结构,包括消息头、消息体等。同时,还需考虑如何处理异常和错误情况。
- 生产者与消费者的实现:在Java微服务中实现生产者和消费者逻辑。生产者负责将消息发送到MQ,而消费者则从MQ中接收并处理消息。
- 配置与优化:根据项目需求对MQ进行配置和优化,如设置消息确认机制、调整重试次数等。同时,还需对集成过程中可能出现的性能瓶颈进行优化。
- 监控与管理:对集成的MQ进行实时监控和管理,以便及时发现问题并进行调优。此外,还需制定相应的维护和管理策略,确保系统的稳定性和可靠性。
SpringAMQP基础使用
AMQP全称是Advanced Message Queuing Protocol,即高级消息队列协议。
使用Spring AMQP的流程可以概括为以下几个步骤:
-
引入依赖:在项目的构建文件(如Maven或Gradle)中添加Spring AMQP的依赖项,以便引入相关的库和类。
-
配置RabbitMQ连接:在Spring应用程序的配置文件中,配置RabbitMQ连接的相关信息,包括主机名、端口号、用户名、密码以及虚拟主机等。
-
创建消息生产者:使用Spring AMQP的模板类(如AmqpTemplate)来发送消息。可以通过注入AmqpTemplate实例,并调用其convertAndSend方法来发送消息。
-
创建消息消费者:使用Spring AMQP的模板类(如AmqpTemplate)来接收消息。可以通过注入AmqpTemplate实例,并调用其receive方法来接收消息。
-
定义消息队列:在RabbitMQ中定义消息队列,以便生产者和消费者可以发送和接收消息。可以使用Spring AMQP的声明方式来定义队列,也可以使用RabbitMQ的管理界面进行手动创建。
-
配置消息转换器:如果生产者和消费者之间的消息类型不匹配,可以使用Spring AMQP提供的消息转换器(如MessageConverter)将消息进行转换。
-
启动应用程序:启动Spring应用程序,并确保RabbitMQ连接已正确配置,然后生产者和消费者就可以开始发送和接收消息了。
实践案例
假设我们正在开发一个在线购物应用,用户可以浏览商品、添加到购物车、下单等。为了提高系统的可扩展性和可靠性,我们决定采用Java微服务和MQ来处理核心业务逻辑。以下是我们的实践案例:
- 用户浏览商品:当用户浏览商品时,前端应用程序通过API调用后端服务的“获取商品列表”接口。后端服务使用RabbitMQ将商品信息发送到“商品信息”主题,多个消费者(例如商品推荐、库存管理等服务)订阅该主题并接收消息进行处理。
- 添加到购物车:当用户将商品添加到购物车时,前端应用程序通过API调用后端服务的“添加商品到购物车”接口。后端服务使用RabbitMQ将购物车更新信息发送到“购物车更新”主题,购物车管理服务订阅该主题并接收消息以更新购物车数据。
- 生成订单:当用户完成购物并生成订单时,前端应用程序通过API调用后端服务的“生成订单”接口。后端服务使用RabbitMQ将订单信息发送到“订单信息”主题,订单处理服务订阅该主题并接收消息以处理订单。同时,支付服务会接收到支付通知,并使用RabbitMQ将支付信息发送到“支付信息”主题,财务部门服务订阅该主题并接收消息以处理支付信息。
- 库存管理:在生成订单时,后端服务会检查商品库存。如果库存不足,后端服务会使用RabbitMQ将库存不足信息发送到“库存通知”主题,库存管理部门服务订阅该主题并接收消息以进行库存补充。
- 实时推荐:推荐服务使用Apache Kafka从“商品信息”主题中获取商品信息,并使用算法进行实时推荐计算。计算结果通过Apache Kafka发送到“推荐结果”主题,前端应用程序订阅该主题并接收消息以更新推荐列表。
- 日志记录与分析:在整个过程中,使用日志记录工具(如ELK Stack)记录所有的操作和事件。通过分析日志,可以了解用户行为、系统性能等信息,以便进行优化和改进。
简单代码示例
这是一个简单的示例,生产者向名为hello
的队列发送一个消息,消费者从该队列接收消息并打印出来。请注意,为了运行此示例,你需要在你的项目中引入RabbitMQ的Java客户端库。
生产者(Producer)
import com.rabbitmq.client.ConnectionFactory; // 导入RabbitMQ的连接工厂类
import com.rabbitmq.client.Connection; // 导入RabbitMQ的连接类
import com.rabbitmq.client.Channel; // 导入RabbitMQ的通道类
import com.rabbitmq.client.DeliverCallback; // 导入RabbitMQ的交付回调接口
public class Send { // 定义一个名为Send的公共类
private final static String QUEUE_NAME = "hello"; // 定义一个私有的静态字符串常量QUEUE_NAME,其值为"hello"
public static void main(String[] argv) throws Exception { // 定义主函数,抛出异常
ConnectionFactory factory = new ConnectionFactory(); // 创建一个ConnectionFactory对象,用于建立连接
factory.setHost("localhost"); // 设置连接的主机地址为本地主机
try (Connection connection = factory.newConnection(); // 创建一个新的连接,使用try-with-resources语句,自动关闭连接
Channel channel = connection.createChannel()) { // 创建一个新的通道,用于发送和接收消息
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 声明一个名为QUEUE_NAME的队列,不持久化,不可交换,不排他,其他参数为null
String message = "Hello World!"; // 创建一个字符串变量message,其值为"Hello World!"
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8")); // 将message变量以UTF-8编码的形式发送到名为QUEUE_NAME的队列中
System.out.println(" [x] Sent '" + message + "'"); // 打印出已发送的消息
}
}
}
消费者(Consumer)
import com.rabbitmq.client.*; // 导入RabbitMQ的相关类库
public class Recv { // 定义一个名为Recv的公共类
private final static String QUEUE_NAME = "hello"; // 定义一个私有的静态字符串常量QUEUE_NAME,其值为"hello"
public static void main(String[] argv) throws Exception { // 定义主函数,抛出异常
ConnectionFactory factory = new ConnectionFactory(); // 创建一个ConnectionFactory对象,用于建立连接
factory.setHost("localhost"); // 设置连接的主机地址为本地主机
Connection connection = factory.newConnection(); // 创建一个新的连接
Channel channel = connection.createChannel(); // 创建一个新的通道,用于发送和接收消息
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 声明一个名为QUEUE_NAME的队列,不持久化,不可交换,不排他,其他参数为null
System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); // 打印出等待消息的提示信息
DeliverCallback deliverCallback = (consumerTag, delivery) -> { // 创建一个DeliverCallback匿名内部类,用于接收消息时调用
String message = new String(delivery.getBody(), "UTF-8"); // 将接收到的消息转换为UTF-8编码的字符串形式
System.out.println(" [x] Received '" + message + "'"); // 打印出接收到的消息
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {}); // 开始从名为QUEUE_NAME的队列中消费消息,自动确认消息,调用deliverCallback回调函数处理消息,其他参数为空
}
}
通过Java微服务和MQ的集成,我们的在线购物应用实现了分布式、可扩展、可靠的处理流程。同时,通过实时推荐等功能提升了用户体验,通过日志记录和分析为运营和开发团队提供了重要的数据支持。
WorkQueue
介绍
工作队列(workqueue)是一种用于在Linux系统中实现中断处理“下半部”的机制。它能够将一些不那么特别紧急且需要一定时间执行的任务延后执行,将工作项封装成函数交给工作队列执行,函数的执行环境从中断环境变成了线程环境。
工作队列内部有一个工作链表(worklist),链表上有很多工作项(work item)节点,可以将工作项简单理解为函数。工作队列内有一个线程一直在轮询工作链表,每次都从工作链表中取出一个工作项,并执行其相关联的函数。当工作队列为空时,线程会被挂起。
在RT-Thread中,有workqueue组件,通过导入头文件#include <ipc/workqueue.h>即可使用。它能够根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。
消费预取机制
exchange交换机
介绍
在RabbitMQ中,交换机(Exchange)是生产者发送消息不会直接将消息投递到队列中,而是先将消息投递到交换机中,再由交换机转发到具体的队列。
Exchange可以分为四种类型:直连交换机(Direct Exchange)、主题交换机(Topic Exchange)、扇形交换机(Fanout Exchange)和组交换机(Group Exchange)。
直连交换机是一种带路由功能的交换机,一个队列会和一个交换机绑定,除此之外再绑定一个routing_key,当消息被发送的时候,需要指定一个binding_key,这个消息被送达交换机的时候,就会被这个交换机送到指定的队列里面去。同样的一个binding_key也是支持应用到多个队列中的。这样当一个交换机绑定多个队列,就会被送到对应的队列去处理。
FanoutExchange
DirectExchange
TopicExchange
Message转换器
[外链图片转存中…(img-HsCzsOkg-1699087191433)]
[外链图片转存中…(img-u9DB8ySe-1699087191434)]
TopicExchange
[外链图片转存中…(img-0a59Gkou-1699087191434)]
Message转换器
[外链图片转存中…(img-LzTmeWA5-1699087191434)]