RabbitMQ的使用
RabbitMQ的使用
相关知识
- AMQP协议
- AMQP是一种开放标准的消息协议,旨在为消息中间件提供统一的通信框架。它的核心特性包括:
- 消息可靠性:支持消息的持久化、确认机制和事务。
- 灵活的路由:通过交换器(Exchange)和绑定(Binding)实现消息的灵活路由。
- 跨平台和语言:AMQP是协议级别的标准,支持多种编程语言和平台。
- AMQP是一种开放标准的消息协议,旨在为消息中间件提供统一的通信框架。它的核心特性包括:
- Spring AMQP Core
- Spring AMQP Core是Spring框架对AMQP协议的抽象和封装,提供了以下核心功能:
- 抽象接口:定义了与AMQP交互的核心接口,如
AmqpTemplate、ConnectionFactory等。 - 消息转换:支持将Java对象转换为消息体(如JSON、XML等)。
- 事务管理:支持声明式事务管理。
- 监听容器:提供了消息监听容器,用于异步接收消息。
- 抽象接口:定义了与AMQP交互的核心接口,如
- Spring AMQP Core是Spring框架对AMQP协议的抽象和封装,提供了以下核心功能:
- Spring RabbitMQ
- Spring RabbitMQ是基于RabbitMQ的Spring AMQP实现,提供了对RabbitMQ的深度集成。RabbitMQ是一个广泛使用的开源消息代理,完全实现了AMQP协议。核心特性:
- RabbitTemplate:用于发送和接收消息的工具类。
- 消息监听器:通过
@RabbitListener注解实现消息的异步消费。 - 声明式配置:通过注解或Java配置声明队列、交换器和绑定。
- 高级特性:支持消息确认、死信队列、延迟队列等。
- Spring RabbitMQ是基于RabbitMQ的Spring AMQP实现,提供了对RabbitMQ的深度集成。RabbitMQ是一个广泛使用的开源消息代理,完全实现了AMQP协议。核心特性:
交换机类型
- RabbitMQ 中的 交换机(Exchange) 是消息路由的核心组件,它负责接收生产者发送的消息,并根据特定的规则将消息路由到一个或多个队列。
- 由于队列中的消息只能被一个消费者处理,所以可以通过交换机同时发给多个队列而无需手动分发
- RabbitMQ 支持多种交换机类型,每种类型有不同的路由行为。
- 以下是 RabbitMQ 中四种主要的交换机类型及其详细说明:
直连交换机(Direct Exchange)
-
路由规则:将消息路由到与 路由键(Routing Key) 完全匹配的队列。
-
使用场景:适用于消息需要精确路由到特定队列的场景。
-
工作原理
- 生产者发送消息时,指定一个路由键(如
order.created)。 - 交换机将消息路由到与路由键完全匹配的绑定队列。
- 生产者发送消息时,指定一个路由键(如
-
示例
-
// 定义直连交换机 @Bean public DirectExchange directExchange() { return new DirectExchange("directExchange"); } // 绑定队列到交换机 @Bean public Binding binding(Queue queue, DirectExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("order.created"); } // 发送消息 rabbitTemplate.convertAndSend("directExchange", "order.created", "Order created!");
-
主题交换机(Topic Exchange)
-
路由规则:将消息路由到与 路由键模式 匹配的队列。
-
使用场景:适用于消息需要根据模式路由到多个队列的场景。
-
工作原理
- 生产者发送消息时,指定一个路由键(如
order.created.us)。 - 交换机将消息路由到与路由键模式匹配的绑定队列。
- 路由键模式支持通配符:
*:匹配一个单词(如order.*可以匹配order.created,但不能匹配order.created.us)。#:匹配零个或多个单词(如order.#可以匹配order.created和order.created.us)。
- 生产者发送消息时,指定一个路由键(如
-
示例
-
@Bean public Queue classicQueue() { return new Queue("classicQueue", true); // 第二个参数表示是否持久化 } // 定义主题交换机 @Bean public TopicExchange topicExchange() { return new TopicExchange("topicExchange"); } // 绑定队列到交换机 @Bean public Binding binding(Queue queue, TopicExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("order.*"); } // 发送消息 rabbitTemplate.convertAndSend("topicExchange", "order.created", "Order created!");
-
扇出交换机(Fanout Exchange)
-
路由规则:将消息路由到所有绑定的队列,忽略路由键。
-
使用场景:适用于消息需要广播到多个队列的场景。
-
工作原理
- 生产者发送消息时,不需要指定路由键。
- 交换机会将消息路由到所有绑定的队列。
-
示例
-
// 定义扇出交换机 @Bean public FanoutExchange fanoutExchange() { return new FanoutExchange("fanoutExchange"); } // 绑定队列到交换机 @Bean public Binding binding(Queue queue, FanoutExchange exchange) { return BindingBuilder.bind(queue).to(exchange); } // 发送消息 rabbitTemplate.convertAndSend("fanoutExchange", "", "Broadcast message!");
-
头交换机(Headers Exchange)
- 路由规则:根据消息的 头信息(Headers) 路由消息,忽略路由键。
- 使用场景:适用于需要根据消息头信息路由消息的场景。
-
工作原理
- 生产者发送消息时,指定一组头信息(如
type=order)。 - 交换机将消息路由到与头信息匹配的绑定队列。
- 匹配规则:
x-match=all:所有头信息都必须匹配。x-match=any:任意一个头信息匹配即可。
- 生产者发送消息时,指定一组头信息(如
-
示例
-
// 定义头交换机 @Bean public HeadersExchange headersExchange() { return new HeadersExchange("headersExchange"); } // 绑定队列到交换机 @Bean public Binding binding(Queue queue, HeadersExchange exchange) { Map<String, Object> headers = new HashMap<>(); headers.put("type", "order"); return BindingBuilder.bind(queue).to(exchange).whereAll(headers).match(); } // 发送消息 MessageProperties properties = new MessageProperties(); properties.setHeader("type", "order"); Message message = new Message("Order message!".getBytes(), properties); rabbitTemplate.send("headersExchange", "", message);
-
默认交换机(Default Exchange)
-
路由规则:将消息路由到与 队列名称 完全匹配的队列。
-
使用场景:RabbitMQ 默认的交换机,适用于简单的消息路由场景。
-
工作原理
- 生产者发送消息时,指定队列名称作为路由键。
- 交换机会将消息路由到与队列名称完全匹配的队列。
-
示例
-
// 发送消息到默认交换机 rabbitTemplate.convertAndSend("myQueue", "Hello, RabbitMQ!");
-
总结
| 交换机类型 | 路由规则 | 使用场景 |
|---|---|---|
| 直连交换机 | 路由键完全匹配 | 精确路由到特定队列 |
| 主题交换机 | 路由键模式匹配 | 根据模式路由到多个队列 |
| 扇出交换机 | 忽略路由键,广播到所有绑定队列 | 广播消息到多个队列 |
| 头交换机 | 根据消息头信息匹配 | 根据消息头信息路由消息 |
| 默认交换机 | 队列名称完全匹配 | 简单的消息路由 |
队列类型
- 在 RabbitMQ 中,队列(Queue) 是存储消息的缓冲区,消费者从队列中获取消息进行处理。RabbitMQ 提供了多种队列类型,每种类型有不同的特性和适用场景。
经典队列(Classic Queue)
-
描述:RabbitMQ 的默认队列类型,基于内存和磁盘存储消息。
-
特点:
- 支持持久化(将消息存储到磁盘,防止消息丢失)。
- 支持消息确认机制(确保消息被成功处理)。
- 支持消息 TTL(Time-To-Live,设置消息的存活时间)。
- 支持死信队列(将无法处理的消息路由到死信队列)。
-
使用场景:适用于大多数消息队列场景。
-
配置示例
-
@Bean public Queue classicQueue() { return new Queue("classicQueue", true); // 第二个参数表示是否持久化 }
-
惰性队列(Lazy Queue)
-
描述:一种优化磁盘使用的队列类型,消息尽可能存储在磁盘中,减少内存占用。
-
特点:
- 消息默认存储在磁盘中,只有在被消费时才会加载到内存。
- 适合处理大量消息且消息处理速度较慢的场景。
- 减少内存压力,但可能会增加消息处理的延迟。
-
使用场景:适用于消息量大、内存有限的场景。
-
配置示例
-
@Bean public Queue lazyQueue() { Map<String, Object> args = new HashMap<>(); args.put("x-queue-mode", "lazy"); // 设置为惰性队列 return new Queue("lazyQueue", true, false, false, args); }
-
优先级队列(Priority Queue)
-
描述:支持消息优先级的队列类型,优先级高的消息会被优先消费。
-
特点:
- 可以为消息设置优先级(0-255,数值越大优先级越高)。
- 高优先级的消息会被优先消费。
- 需要消费者支持优先级处理。
-
使用场景:适用于需要优先处理某些消息的场景,如订单系统中的紧急订单。
-
配置示例
-
@Bean public Queue priorityQueue() { Map<String, Object> args = new HashMap<>(); args.put("x-max-priority", 10); // 设置最大优先级为 10 return new Queue("priorityQueue", true, false, false, args); }
-
-
发送优先级消息
-
MessageProperties properties = new MessageProperties(); properties.setPriority(5); // 设置消息优先级 Message message = new Message("High priority message!".getBytes(), properties); rabbitTemplate.send("priorityQueue", message);
-
死信队列(Dead Letter Queue, DLQ)
-
描述:用于存储无法被正常处理的消息的队列。
-
特点:
- 当消息被拒绝、过期或队列达到最大长度时,消息会被路由到死信队列。
- 死信队列可以用于分析和处理异常消息。
-
使用场景:适用于需要处理异常消息的场景。
-
配置示例
-
@Bean public Queue mainQueue() { Map<String, Object> args = new HashMap<>(); args.put("x-dead-letter-exchange", "dlxExchange"); // 设置死信交换机 args.put("x-dead-letter-routing-key", "dlxRoutingKey"); // 设置死信路由键 return new Queue("mainQueue", true, false, false, args); } @Bean public Queue deadLetterQueue() { return new Queue("deadLetterQueue", true); } @Bean public DirectExchange dlxExchange() { return new DirectExchange("dlxExchange"); } @Bean public Binding dlxBinding(Queue deadLetterQueue, DirectExchange dlxExchange) { return BindingBuilder.bind(deadLetterQueue).to(dlxExchange).with("dlxRoutingKey"); }
-
延迟队列(Delayed Queue)
-
描述:支持延迟消息的队列类型,消息在指定的延迟时间后才会被消费。
-
特点:
- 消息在达到延迟时间后才会进入队列。
- 需要安装 RabbitMQ 的延迟消息插件(
rabbitmq_delayed_message_exchange)。
-
使用场景:适用于需要延迟处理消息的场景,如订单超时未支付取消。
-
配置示例
-
@Bean public CustomExchange delayedExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); // 设置延迟交换机的类型 return new CustomExchange("delayedExchange", "x-delayed-message", true, false, args); } // x-delayed-type:设置延迟交换机的类型(如 direct、topic 等)。 // x-delayed-message:表示这是一个延迟交换机。 @Bean public Queue delayedQueue() { return new Queue("delayedQueue", true); } @Bean public Binding delayedBinding(Queue delayedQueue, CustomExchange delayedExchange) { return BindingBuilder.bind(delayedQueue).to(delayedExchange).with("delayedRoutingKey").noargs(); } -
使用
ExchangeBuilder创建延迟交换机-
@Configuration public class RabbitConfig { @Bean public DirectExchange delayedExchange() { return ExchangeBuilder .directExchange("delayedExchange") // 交换机名称 .durable(true) // 持久化 .delayed() // 标记为延迟交换机(自动添加 x-delayed-type) .build(); } }
-
-
使用
@RabbitListener创建延迟交换机-
@RabbitListener( bindings = @QueueBinding( value = @Queue(name = "delay", durable = "true" ), exchange = @Exchange(name = "exchange", type = "direct", delayed="true"),//标记为延迟交换机 key = "delay" )) public void listen1(String message){ System.out.println("延迟交换机"); }
-
-
-
发送延迟消息
-
MessageProperties properties = new MessageProperties(); properties.setHeader("x-delay", 5000); // 设置延迟时间为 5 秒 Message message = new Message("Delayed message!".getBytes(), properties); rabbitTemplate.send("delayedExchange", "delayedRoutingKey", message);
-
-
延迟消息的存储
- 延迟消息保存在交换机中,而不是队列中。
- 当消息发送到延迟交换机时,交换机会根据消息的
x-delay头信息计算出消息的投递时间,并将消息存储在内部的一个延迟队列中。 - 当消息的延迟时间到达后,交换机会将消息路由到目标队列,供消费者消费。
-
消息 TTL 和死信队列 是一种替代方案,但实现较为复杂。
使用RabbitMQ
启动RabbitMQ
添加依赖
-
在
pom.xml中添加Spring AMQP和RabbitMQ的依赖: -
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>spring-boot-starter-amqp已经包含了 RabbitMQ 的依赖(如amqp-client和spring-rabbit)
配置连接信息
-
在
application.properties中配置RabbitMQ连接信息: -
# RabbitMQ 服务器地址 spring.rabbitmq.host=localhost # RabbitMQ 服务器端口(默认是 5672) spring.rabbitmq.port=5672 # RabbitMQ 用户名(默认是 guest) spring.rabbitmq.username=guest # RabbitMQ 密码(默认是 guest) spring.rabbitmq.password=guest # 虚拟主机(默认是 /) spring.rabbitmq.virtual-host=/ # 连接超时时间(单位:毫秒) spring.rabbitmq.connection-timeout=5000 # 是否启用 SSL(默认是 false) spring.rabbitmq.ssl.enabled=false
定义队列、交换机和绑定
队列
-
在 Spring AMQP 中,
Queue的构造函数有多个参数,用于定义队列的属性和行为。 -
public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)name- 类型:
String - 含义:队列的名称。
- 类型:
durable- 类型:
boolean - 含义:队列是否持久化。
true:队列是持久化的,即使 RabbitMQ 服务器重启,队列仍然存在。false:队列是非持久化的,RabbitMQ 服务器重启后,队列会被删除。
- 类型:
exclusive- 类型:
boolean - 含义:队列是否是独占的。
true:队列是独占的,只能被当前连接使用。当连接关闭时,队列会被自动删除。false:队列是非独占的,可以被多个连接使用。
- 类型:
autoDelete- 类型:
boolean - 含义:队列是否自动删除。
true:当最后一个消费者断开连接时,队列会被自动删除。false:队列不会自动删除,即使没有消费者。
- 类型:
arguments- 类型:
Map<String, Object> - 含义:队列的附加参数,用于设置队列的高级特性。
- 例如:设置队列为惰性队列(
x-queue-mode=lazy)、设置消息 TTL(x-message-ttl)等。
- 例如:设置队列为惰性队列(
- 类型:
-
队列的创建方式
-
在 Spring 配置类中,使用
@Bean注解定义队列。 -
在 Spring AMQP 中,
Queue类是用于定义 RabbitMQ 队列的核心类。它位于org.springframework.amqp.core包下。 -
import org.springframework.amqp.core.Queue; @Configuration public class RabbitMQConfig { @Bean public Queue myQueue() { return new Queue("myQueue", true, false, false); } }
-
-
队列的高级配置
- 通过
arguments参数,可以为队列设置高级特性。
- 通过
| 参数名 | 类型 | 说明 |
|---|---|---|
x-message-ttl |
Integer |
消息的存活时间(Time-To-Live),单位为毫秒。 |
x-max-length |
Integer |
队列的最大消息数量,超过该数量时,旧消息会被丢弃。 |
x-max-length-bytes |
Integer |
队列的最大消息总大小(字节),超过该大小时,旧消息会被丢弃。 |
x-dead-letter-exchange |
String |
死信交换机名称,用于将无法处理的消息路由到死信队列。 |
x-dead-letter-routing-key |
String |
死信路由键,用于指定死信消息的路由键。 |
x-max-priority |
Integer |
队列支持的最大优先级(0-255),用于优先级队列。 |
x-queue-mode |
String |
队列模式,可选值为 default(默认)或 lazy(惰性队列)。 |
-
@Configuration public class RabbitMQConfig { @Bean public Queue myQueue() { Map<String, Object> args = new HashMap<>(); args.put("x-message-ttl", 60000); // 消息存活时间为 60 秒 args.put("x-max-length", 1000); // 队列最大消息数量为 1000 args.put("x-dead-letter-exchange", "dlxExchange"); // 设置死信交换机 args.put("x-dead-letter-routing-key", "dlxRoutingKey"); // 设置死信路由键 return new Queue("myQueue", true, false, false, args); } } -
QueueBuilder 创建队列
-
Queue customQueue = QueueBuilder.durable("customQueue") .autoDelete() // 当所有消费者断开连接后自动删除 .exclusive() // 排他队列,仅限此连接使用 .withArgument("x-message-ttl", 60000) // 消息存活时间60秒 .withArgument("x-max-length", 1000) // 队列最大消息数 .withArgument("x-max-length-bytes", 10485760) // 队列最大容量(10MB) .withArgument("x-dead-letter-exchange", "dlx.exchange") // 死信交换机 .withArgument("x-dead-letter-routing-key", "dlx.routingKey") // 死信路由键 .build();
-
交换机
-
交换机的构造函数参数如下
-
public DirectExchange(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments) -
其中和队列对应的参数含义相同
-
-
在 Spring 配置类中,使用
@Bean注解定义交换机。-
@Configuration public class RabbitMQConfig { // 创建直连交换机 @Bean public DirectExchange directExchange() { return new DirectExchange("directExchange"); } // 创建主题交换机 @Bean public TopicExchange topicExchange() { return new TopicExchange("topicExchange"); } // 创建扇出交换机 @Bean public FanoutExchange fanoutExchange() { return new FanoutExchange("fanoutExchange"); } // 创建头交换机 @Bean public HeadersExchange headersExchange() { return new HeadersExchange("headersExchange"); } } -
map中的常用参数
参数名 类型 说明 x-delayed-typeString延迟交换机的类型(如 direct、topic等)。alternate-exchangeString备用交换机名称,用于处理无法路由的消息。 -
-
ExchangeBuilder 创建交换机
-
Exchange customExchange = ExchangeBuilder.topicExchange("advanced.exchange") .durable(true) // 持久化交换机 .autoDelete() // 当所有队列都断开连接后自动删除 .internal() // 内部交换机,不能直接发布消息 .withArgument("alternate-exchange", "ae.exchange") // 备用交换机 .delayed() // 支持延迟消息(需要插件) .build();
-
绑定
-
创建完成队列和交换机后则需要将队列和交换机进行绑定
-
队列之间不能同名:在同一个虚拟主机中,队列的名称必须是唯一的。
-
交换机之间不能同名:在同一个虚拟主机中,交换机的名称也必须是唯一的。
-
在 Spring 配置类中,使用
@Bean注解定义绑定。-
//直连交换机绑定 @Bean public Binding directBinding(Queue myQueue, DirectExchange directExchange) { return BindingBuilder.bind(myQueue) .to(directExchange) .with("routingKey"); } //主题交换机绑定 @Bean public Binding topicBinding(Queue myQueue, TopicExchange topicExchange) { return BindingBuilder.bind(myQueue) .to(topicExchange) .with("routing.*"); } //扇出交换机绑定 @Bean public Binding fanoutBinding(Queue myQueue, FanoutExchange fanoutExchange) { return BindingBuilder.bind(myQueue) .to(fanoutExchange); } //头交换机绑定 @Bean public Binding headersBinding(Queue myQueue, HeadersExchange headersExchange) { Map<String, Object> args = new HashMap<>(); args.put("key1", "value1"); // 头信息键值对 args.put("x-match", "all"); // 匹配规则 return BindingBuilder.bind(myQueue) .to(headersExchange) .whereAll(args) .match(); }
-
注解方式进行绑定
-
@RabbitListener注解不仅可以用于监听队列,还可以通过bindings属性直接定义队列、交换机以及绑定关系。 -
注解属性
queues:指定监听的队列名称。bindings:用于定义队列、交换机以及绑定关系。
-
在
@RabbitListener的bindings属性中,可以使用@QueueBinding注解来定义绑定关系。 -
@QueueBinding注解的属性value:指定队列(@Queue注解)。exchange:指定交换机(@Exchange注解)。key:指定路由键。
-
@Configuration @EnableRabbit // 启用 RabbitMQ 注解支持 public class RabbitMQConfig { // 使用 @RabbitListener 注解绑定队列和交换机 @RabbitListener( bindings = @QueueBinding( value = @Queue(value = "myQueue", durable = "true"), // 定义队列 exchange = @Exchange(value = "myExchange", type = ExchangeTypes.DIRECT), // 定义交换机 key = "myRoutingKey" // 定义路由键 ) ) public void receiveMessage(String message) { System.out.println("Received message: " + message); } } -
适用于需要快速定义队列、交换机和绑定关系的场景。
-
如果 RabbitMQ 中已经存在与
@Exchange注解中指定的名称(value)相同的交换机,RabbitMQ 会直接绑定到该交换机,而不会重新创建。- 触发条件
- RabbitMQ 中已经存在名为
annoExchange的交换机。 - 已有交换机的属性(如类型、持久化等)与
@Exchange注解中的配置完全一致。
- RabbitMQ 中已经存在名为
- 触发条件
发送消息
-
使用
RabbitTemplate发送消息到队列。-
@Service public class MessageSender { @Autowired private AmqpTemplate rabbitTemplate; }
-
-
发送消息到指定的交换器和路由键
-
/** * 发送消息到指定的交换器和路由键 * * @param exchange 交换器名称 * @param routingKey 路由键 * @param message 消息内容 */ public void sendMessage(String exchange, String routingKey, String message) { rabbitTemplate.convertAndSend(exchange, routingKey, message); System.out.println("Sent message: " + message); }
-
-
直接发送到队列
-
/** * 发送消息到默认的交换器(直接发送到队列) * * @param queueName 队列名称 * @param message 消息内容 */ public void sendMessageToQueue(String queueName, String message) { rabbitTemplate.convertAndSend(queueName, message); System.out.println("Sent message to queue: " + message); }
-
-
在需要发送消息的地方调用
MessageSender的方法即可。 -
发送消息的内容不能为空
-
rabbitTemplate.convertAndSend("exchange","lazy",""); -
上述代码会报错
-
Spring AMQP 默认使用
SimpleMessageConverter,它可能无法正确处理空消息。 -
如果消息体为空,消费者在尝试将空消息转换为目标类型时可能会抛出异常。
-
也就是说可以正常发送,无法正常接收
-
-
Message-
在 RabbitMQ 中,
Message是 Spring AMQP 提供的一个类,用于封装消息的内容和属性。Message对象包含两部分:- 消息体(Body):消息的实际内容,通常是字节数组(
byte[])。 - 消息属性(Properties):消息的元数据,如消息头、优先级、过期时间等。类型为:
MessageProperties
- 消息体(Body):消息的实际内容,通常是字节数组(
-
Message类的主要作用包括:- 封装消息内容:
- 将消息的实际数据(如字符串、JSON、二进制数据)封装为字节数组。
- 存储消息属性:
- 通过
MessageProperties存储消息的元数据,如消息头、内容类型、优先级等。
- 通过
- 与 RabbitMQ 交互:
RabbitTemplate使用Message对象发送消息。@RabbitListener使用Message对象接收消息。
- 封装消息内容:
-
发送指定格式内容的消息
-
@Service public class MessageSender { @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String exchange, String routingKey, String content) { // 创建 MessageProperties MessageProperties properties = new MessageProperties(); properties.setContentType("text/plain"); // 设置内容类型为纯文本 // 创建 Message 对象 Message message = new Message(content.getBytes(), properties); // 发送消息 rabbitTemplate.send(exchange, routingKey, message); } }
-
-
使用
Message类接收消息-
可以得到设置的各种信息
-
@Component public class MessageReceiver { @RabbitListener(queues = "myQueue") public void receiveMessage(Message message) { // 获取消息体 String body = new String(message.getBody()); System.out.println("Received message body: " + body); // 获取消息属性 MessageProperties properties = message.getMessageProperties(); System.out.println("Content type: " + properties.getContentType()); System.out.println("Headers: " + properties.getHeaders()); } }
-
-
-
通过
MessageProperties可以设置的属性
| 属性名 | 类型 | 描述 |
|---|---|---|
contentType |
String | 消息的内容类型(如 application/json、text/plain)。 |
contentEncoding |
String | 消息内容的编码方式(如 UTF-8)。 |
headers |
Map<String, Object> | 自定义消息头,用于传递额外的元数据。 |
deliveryMode |
Integer | 消息的持久化模式:1(非持久化)或 2(持久化)。 |
priority |
Integer | 消息的优先级(0-9),数值越大优先级越高。 |
correlationId |
String | 用于关联消息的 ID,通常用于 RPC 模式。 |
replyTo |
String | 用于指定回复消息的目标队列,通常用于 RPC 模式。 |
expiration |
String | 消息的过期时间(以毫秒为单位),超过此时间未被消费的消息会被丢弃。 当消息和队列都设置了过期时间时,RabbitMQ 会根据更早过期的时间来决定消息的过期行为。 |
messageId |
String | 消息的唯一标识符。 |
timestamp |
Date | 消息的时间戳。 |
type |
String | 消息的类型,通常用于区分不同的消息类别。 |
userId |
String | 消息的生产者用户 ID。 |
appId |
String | 生产者的应用标识符。 |
clusterId |
String | 集群 ID,用于标识消息的来源集群。 |
接收消息
-
使用
@RabbitListener注解接收消息 -
@RabbitListener是 Spring AMQP 提供的一个注解,用于声明一个方法作为消息监听器。 -
该类需要交由Spring容器进行管理
-
@Component public class MessageReceiver { // 监听指定的队列 @RabbitListener(queues = "myQueue") public void receiveMessage(String message) { System.out.println("Received message: " + message); } } -
@RabbitListener注解标记的方法会自动监听指定的队列(如myQueue)。 -
当队列中有消息时,Spring 会调用该方法,并将消息体作为参数传入。
-
-
可以一个队列上绑定多个消费者,加快消息处理的速度,MQ默认将队列中的消息按照预取数量分发给各个消费者进行处理
-
预取数量(Prefetch Count)
-
预取数量的主要作用是 平衡消息处理的效率和消费者的负载
-
在 RabbitMQ 中,预取数量的默认值是
0,表示没有限制。这意味着:- RabbitMQ 会尽可能多地将消息推送给消费者,直到消费者无法处理更多消息为止。
-
工作原理
- 当消费者连接到队列时,RabbitMQ 会根据预取数量一次性推送多条消息给消费者。
- 消费者在处理完这些消息后,需要向 RabbitMQ 发送确认(ACK),RabbitMQ 才会推送新的消息。
- 如果消费者在处理消息时崩溃或断开连接,未确认的消息会被重新入队,供其他消费者处理。
-
预取数量的配置
-
在
application.properties或application.yml中配置: -
spring.rabbitmq.listener.simple.prefetch=10 # 每个消费者最多预取 10 条消息
-
-
发送和接收复杂对象
-
在 Spring 框架中使用
RabbitTemplate发送复杂对象(如User)时,如果配置 JSON 消息转换器(如Jackson2JsonMessageConverter)作为RabbitMQ 的消息转换器的话,Spring 会自动完成以下工作:- 发送时:
- 将 Java 对象(如
User)序列化为 JSON 格式。 - 将 JSON 数据作为消息体发送到 RabbitMQ。
- 将 Java 对象(如
- 接收时:
- 从 RabbitMQ 中获取消息体(JSON 格式)。
- 将 JSON 数据反序列化为 Java 对象(如
User)。
- 发送时:
-
spring-boot-starter-web起步依赖中配置了这个JSON消息转换器-
然而,Spring Boot 的自动配置并不会自动将
Jackson2JsonMessageConverter配置为 RabbitMQ 的消息转换器。 -
Spring Boot 不会自动将 Jackson 的 JSON 转换器应用到 RabbitMQ 中,因为 RabbitMQ 的消息格式不仅限于 JSON(还支持二进制、文本等)。
-
当发送对象时,如果对象类没有实现可序列化接口则会报错
-
java.lang.IllegalArgumentException: SimpleMessageConverter only supports String, byte[] and Serializable payloads, received: Spring.Pojo.User
-
-
-
在 Spring MVC 中,
@ResponseBody注解用于将方法的返回值直接写入 HTTP 响应体中。Spring MVC 默认使用 Jackson 作为 JSON 序列化库,因此它会自动将对象转换为 JSON 格式。- 为什么不需要实现
Serializable? - Jackson 的职责:Jackson 是一个独立的 JSON 序列化库,它通过反射直接访问对象的字段和方法,不需要对象实现
Serializable接口。 - HTTP 协议的特性:HTTP 响应体通常是文本格式(如 JSON、XML),Spring MVC 默认使用 Jackson 将对象序列化为 JSON 字符串,而不是二进制格式。
- 为什么不需要实现
-
在 RabbitMQ 中,消息的传输是基于 AMQP 协议的,消息体可以是任意格式(如二进制、文本、JSON 等)。Spring AMQP 默认使用
SimpleMessageConverter来处理消息。- 为什么需要实现
Serializable? SimpleMessageConverter的职责:SimpleMessageConverter是 Spring AMQP 的默认消息转换器,它支持以下类型的消息体:String(字符串)byte[](字节数组)- 实现了
Serializable接口的对象
- 二进制传输:RabbitMQ 的消息体通常是二进制格式,
SimpleMessageConverter使用 Java 的序列化机制将对象转换为字节数组。因此,对象必须实现Serializable接口。
- 为什么需要实现
-
使用
Jackson2JsonMessageConverter替代SimpleMessageConverter后,二进制数据格式的消息可能会出现问题。这是因为Jackson2JsonMessageConverter是专门用于 JSON 序列化和反序列化的,它默认会将消息体当作 JSON 处理。如果尝试发送或接收二进制数据(如byte[]),Jackson2JsonMessageConverter可能会无法正确处理。
消息转换器
-
消息转换器(MessageConverter) 是一个核心组件,它负责将 Java 对象转换为消息体(如 JSON、二进制等),以及将消息体转换回 Java 对象。Spring AMQP 提供了多种内置的消息转换器,同时也支持自定义转换器。
-
消息转换器的作用
- 消息转换器的主要功能是:
- 发送消息时:
- 将 Java 对象转换为消息体(如 JSON、二进制等)。
- 设置消息的属性(如
contentType)。
- 接收消息时:
- 将消息体转换回 Java 对象。
- 根据消息的属性(如
contentType)选择合适的转换逻辑。
- 发送消息时:
- 消息转换器的主要功能是:
-
内置消息转换器
- Spring AMQP 提供了以下几种内置的消息转换器:
SimpleMessageConverter- 默认转换器:如果没有显式配置其他转换器,Spring AMQP 会使用
SimpleMessageConverter。 - 支持的类型:
String(字符串)byte[](字节数组)- 实现了
Serializable接口的对象
- 特点:
- 对于
String和byte[],直接将其作为消息体。 - 对于实现了
Serializable接口的对象,使用 Java 序列化机制将其转换为字节数组。
- 对于
- 局限性:
- 不支持 JSON 格式。
- 要求对象实现
Serializable接口。
- 默认转换器:如果没有显式配置其他转换器,Spring AMQP 会使用
Jackson2JsonMessageConverter- JSON 转换器:将 Java 对象序列化为 JSON 格式,或将 JSON 格式的消息反序列化为 Java 对象。
- 支持的类型:
- 任意 Java 对象(不需要实现
Serializable接口)。
- 任意 Java 对象(不需要实现
- 特点:
- 使用 Jackson 库进行 JSON 序列化和反序列化。
- 支持复杂的对象结构(如嵌套对象、集合等)。
- 局限性:
- 仅支持 JSON 格式,无法处理二进制数据。
ContentTypeDelegatingMessageConverter- 组合转换器:根据消息的
contentType动态选择不同的转换器。 - 特点:
- 可以同时支持多种消息格式(如 JSON、二进制、文本等)。
- 需要显式配置支持的转换器。
- 组合转换器:根据消息的
- Spring AMQP 提供了以下几种内置的消息转换器:
-
配置消息转换器
-
在 Spring Boot 中,可以通过配置类定义消息转换器。
-
当手动配置一个
MessageConverterBean 时,它会覆盖默认的SimpleMessageConverter。-
配置
Jackson2JsonMessageConverter -
导入jackson依赖
-
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>-
然后配置JSON转换器
-
@Configuration public class RabbitMQConfig { @Bean public MessageConverter jsonMessageConverter() { return new Jackson2JsonMessageConverter(); } }
-
-
配置
ContentTypeDelegatingMessageConverter-
@Configuration public class RabbitMQConfig { @Bean public MessageConverter compositeMessageConverter() { ContentTypeDelegatingMessageConverter converter = new ContentTypeDelegatingMessageConverter(); // 注册 JSON 转换器 converter.addDelegate("application/json", new Jackson2JsonMessageConverter()); // 注册二进制转换器 converter.addDelegate("application/octet-stream", new SimpleMessageConverter()); return converter; } }
-
-
-
-
发送消息
-
配置好消息转换器后,可以使用
RabbitTemplate指定发送消息的类型。 -
@Service public class MessageSender { @Autowired private RabbitTemplate rabbitTemplate; public void sendBinaryMessage(String queueName, byte[] data) { rabbitTemplate.convertAndSend(queueName, data, message -> { MessageProperties properties = message.getMessageProperties(); properties.setContentType("application/octet-stream"); // 指定内容类型为二进制 return message; }); System.out.println("Sent binary message: " + new String(data)); } }
-
-
接收消息
-
在接收消息时,Spring 会根据消息的
contentType自动选择合适的转换器。 -
@Component public class MessageReceiver { @RabbitListener(queues = "myQueue") public void handleMessage(Object payload) { if (payload instanceof byte[]) { System.out.println("Received binary message: " + new String((byte[]) payload)); } else { System.out.println("Received JSON message: " + payload); } } }
-

浙公网安备 33010602011771号