MQ消息队列:异步处理的最佳实践

一、MQ介绍

1. 同步调用与异步调用


微服务一旦拆分,必然涉及到服务之间的相互调用,目前我们服务之间调用采用的都是基于OpenFeign的调用。这种调用中,调用者发起请求后需要等待服务提供者执行业务返回结果后,才能继续执行后面的业务。也就是说调用者在调用过程中处于阻塞状态,因此我们成这种调用方式为同步调用,也可以叫同步通讯。但在很多场景下,我们可能需要采用异步调用的方式。

程序里所有的通信,有两种形式:同步通讯和异步通讯。

在这里插入图片描述

解读:

  • 同步调用:就如同打视频电话,双方的交互都是实时的。因此同一时刻你只能跟一个人打视频电话。
  • 异步调用:就如同发微信聊天,双方的交互不是实时的,你不需要立刻给对方回应。因此你可以多线操作,同时跟多人聊天。

两种调用方式各有优缺点:

同步调用
优点: 时效性强,等待到结果才返回
缺点: 拓展性差、性能差、级联失败

异步调用
优点:

  • 耦合度低,扩展性强
  • 异步调用,无需等待,性能好
  • 故障隔离,下游服务故障不影响上有业务
  • 缓存消息,流量削峰填谷

缺点:

  • 不能立即得到调用结果,时效性差
  • 不确定下游业务执行是否成功
  • 业务安全依赖Broker的可靠性
  • 业务复杂度增加:防止消息丢失、消息重复,要保证数据的一致性等等问题
  • 系统架构复杂度增加:必须保证MQ的高可用

2. MQ介绍

Message Queue,消息对列
​消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。

MQ的作用:服务之间数据交互时的一种方式。和Feign对比

  • 异步:实现服务之间异步通信
  • 削峰:MQ可以堆积消息,可以应对流量洪峰,实现流量的削峰填谷
  • 解耦:实现服务之间的耦合性降低

常见的MQ有:

  • ActiveMQ
  • RabbitMQ
  • RocketMQ
  • Kafka

几种常见MQ的对比:

-RabbitMQActiveMQRocketMQKafka
公司/社区RabbitApache阿里Apache
开发语言ErlangJavaJavaScala&Java
协议支持AMQP,XMPP,SMTP,STOMPOpenWire,STOMP,REST,XMPP,AMQP自定义协议自定义协议
可用性一般
单机吞吐量一般非常高
消息延迟微秒级毫秒级毫秒级毫秒以内
消息可靠性一般一般
  1. 追求可用性:Kafka、 RocketMQ 、RabbitMQ
  2. 追求可靠性:RabbitMQ、RocketMQ
  3. 追求吞吐能力:RocketMQ、Kafka
  4. 追求消息低延迟:RabbitMQ、Kafka

二、RabbitMQ 介绍

AMQP,Advanced Message Queuing Protocol,高级消息队列,是一种网络协议。它是应用层协议的一个开发标准,为面向消息的中间件而设计。
RabbitMQ 是一个开源的消息代理软件,也被称为消息队列系统。它支持多种消息协议,并且能够被易于地集成到各种应用程序中。

以下是 RabbitMQ 的一些关键特性和概念:

核心特性

  1. 多协议支持:RabbitMQ 支持多种消息协议,包括 AMQP 0-9-1、AMQP 1.0、MQTT、STOMP 等。
  2. 高可用性:通过集群和镜像队列支持,RabbitMQ 可以提供高可用性的消息服务。
  3. 灵活的路由:RabbitMQ 拥有灵活的消息路由功能,可以根据需要将消息路由到不同的队列。
  4. 消息持久化:支持将消息存储在磁盘上,确保消息不会因为系统崩溃而丢失。
  5. 确认机制:提供消息确认机制,确保消息被消费者成功处理。
  6. 异步处理:支持异步消息处理,提高应用程序性能。
  7. 扩展性:可以通过添加更多的节点来扩展 RabbitMQ 集群。
  8. 管理界面:提供了一个易于使用的管理界面,用于监控和管理消息队列。

基本概念

  1. 生产者(Producer):发送消息的应用程序或服务。
  2. 消费者(Consumer):接收消息的应用程序或服务。
  3. 队列(Queue):消息的存储地,消费者从队列中获取消息。
  4. 交换机(Exchange):接收生产者发送的消息,并将消息路由到一个或多个队列。
  5. 绑定(Binding):定义了交换机和队列之间的关系,以及如何路由消息。
  6. 虚拟主机(VHost):RabbitMQ 的一个命名空间,拥有自己的队列、交换机和权限设置。
  7. 用户权限(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 使用示例:

  1. 添加依赖
 <!--AMQP依赖,包含RabbitMQ-->
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  1. 配置RabbitMQ 连接信息
spring:
  application:
    name: demo-producer
  rabbitmq:
    host: 192.168.119.129 #RabbitMQ服务的ip
    port: 5672            #RabbitMQ服务的端口
    username: xxxxxx 	  #RabbitMQ的帐号
    password: xxxxxx 	  #RabbitMQ的密码
  1. 创建配置类
// 创建一个配置类来配置消息的交换机(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");
    }
}
  1. 创建消息生产者
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);
    }
}
  1. 创建消息消费者
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);
    }
}
  1. 使用生产者和消费者
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 提供了多种消息转换器,以支持不同的数据格式和序列化机制。

消息转换器的作用:
  1. 序列化:在发送消息时,将对象转换为可以传输的格式(如 JSON、XML 等)。
  2. 反序列化:在接收消息时,将传输的数据转换回对象。
Spring AMQP 提供的消息转换器:
  1. SimpleMessageConverter:默认的消息转换器,适用于简单的字符串消息。
  2. Jackson2JsonMessageConverter:使用 Jackson 库进行 JSON 序列化和反序列化。
  3. MarshallingMessageConverter:使用 Spring 的 Marshalling 框架,可以与 JAX-B 或其他 Object Mapping 库一起使用。
  4. 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 对象,并传递给 MessageHandlerhandleMessage 方法进行处理。

请注意,这个示例假设你已经配置了 RabbitMQ 连接和队列。此外,你可能需要根据你的具体需求调整配置和组件。使用消息转换器可以大大提高消息处理的灵活性和可维护性。

四、总结

AMQP 是一个协议标准,定义了消息队列系统中的消息传递方式。RabbitMQ 是遵循这一标准的一个具体实现,提供了一套完整的消息队列服务。使用 RabbitMQ 和 AMQP,开发者可以构建可靠、灵活和高效的分布式消息系统。

  • 60
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值