1. 引言
本文将介绍如何使用 RabbitMQ 注解,通过设置 TTL 和死信队列,实现纯注解方式的延迟消息队列。另外:我们可以使用 RabbitMQ 官方的延迟队列插件来实现延迟队列,这里不说明,感兴趣可以自己去学习。
-
TTL + 死信队列 方式:
- 适用于更灵活的场景,允许在代码层面直接控制延迟队列的配置。
- 适用于小型项目或者特定队列需要个性化配置的情况
-
插件方式:
- 适用于需要全局配置延迟队列机制的情况。
- 适用于不希望将延迟队列的配置与代码混合的情况。
2. TTL 的概述
消息的 Time-To-Live(TTL)是消息在队列中存活的时间。在 RabbitMQ 中,我们可以使用 TTL 控制消息的生命周期,从而实现延迟消息的效果。
3. 死信队列的介绍
死信队列用于处理过期或被拒绝的消息。通过设置死信队列,我们可以有效地处理延迟消息队列中的失效消息,确保系统的可靠性。
4. 为什么需要延迟消息队列
延迟消息队列在任务调度、定时任务等场景中具有广泛应用。它使得我们能够以更灵活的方式处理需要延迟执行的业务逻辑。下面简单说个例子。
订单超时处理:
- 场景描述: 在电子商务中,当用户下单后,有时需要等待用户支付。如果用户在规定的时间内没有完成支付,可能需要取消订单。
- 延迟消息应用: 将订单信息发送到延迟消息队列,并设置合适的延迟时间。如果在规定时间内用户未支付,延迟队列会触发订单超时的处理逻辑
5. RabbitMQ 注解的介绍
RabbitMQ 注解是一种简化 RabbitMQ 操作的方式,通过注解可以更轻松地配置队列、交换机和消息监听器。常见的注解包括 @Queue
、@Exchange
、@RabbitListener
等。
6. 纯注解实现延迟消息队列
在这一部分,我将演示如何使用 RabbitMQ 注解实现延迟消息队列。我们将配置队列的 TTL 和死信队列,以确保消息在一定时间后被正确处理。
7. 示例代码
以下是一个简化的示例代码,展示了如何使用 RabbitMQ 注解实现延迟消息队列。
特别说明:下面的示例代码中,消费者的参数 Message,使用的是下面的Message包。
arduino
import org.springframework.messaging.Message;
定义正常的业务交换机和队列,但不开启此消费者(不开启消费者,队列中的消息无法处理,等过了TTL,消息就会被发送到死信队列)
- 设置死信交换机和死信key,指定队列的消息 TTL
注意 :在@RabbitListener
注解中,可以使用autoStartup
属性来控制消费者的启动和关闭。默认情况下,autoStartup
属性的值为true
,表示消费者将会自动启动。我们可以将其设置为false
,以关闭消费者进行测试。
less
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "nomal_queue",
durable="true", // 持久化可设置为 true
arguments = {
@Argument(name = "x-dead-letter-exchange",value = "dead_letter_exchange"), // 死信交换机
@Argument(name = "x-dead-letter-routing-key",value = "dead_letter_key"), // 死信key
@Argument(name = "x-message-ttl",value = "10000", type = "java.lang.Integer"), // todo: TTL设置为 10000ms, 即 10秒。在这里是设置整个队列的消息TTL,也可以在生产者里定义单个消息的TTL
}),
exchange = @Exchange(value = "nomal_exchange",
durable="true",
ignoreDeclarationExceptions = "true"),
key = {"nomal_key"}
),
autoStartup = "false" // 设置为false以关闭消费者,下面的消费代码不会执行
)
@RabbitHandler
public void onNomalHandler(Message message, Channel channel) throws Exception {
// 打印日志
log.info("onNomalHandler 消息id为={}, 接收到的Payload={}",messageId, message.getPayload());
// 模拟执行过程出异常,将消息放回队列
Thread.sleep(1000);
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
// 手工ACK
// channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
// 拒绝消息
// channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
}
定义死信交换机和队列
less
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "dead_letter_queue",
durable="true"),
exchange = @Exchange(value = "dead_letter_exchange",
durable="true",
ignoreDeclarationExceptions = "true"),
key = {"dead_letter_key"}
))
@RabbitHandler
public void onDelayHandler(Message message, Channel channel) throws Exception {
String messageId = message.getHeaders().getId().toString();
// 打印日志,调式
log.info("onDelayHandler 死信队列 消息id为={}, 接收到的Payload={}", messageId, message.getPayload());
// 手工ACK
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
定义生产者,示例通过编写测试单元
typescript
@Test
void testDelayQueue(){
String msg = "this is a test message";
rabbitTemplate.convertAndSend("nomal_exchange", "nomal_key", msg);
}
8. 总结
通过本文,我们了解了如何使用 RabbitMQ 注解、TTL 和死信队列实现延迟消息队列。这种纯注解的方式使得配置更加简便,提高了代码的可读性和可维护性。希望读者能够通过这些知识,更好地利用 RabbitMQ 构建灵活而可靠的消息系统。
9. 参考资料
scss
[https://www.rabbitmq.com/documentation.html](url)
[https://docs.spring.io/spring-amqp/docs/current/reference/html/](url)