Springboot+死信实现RabbitMQ延迟队列
原理
生产者把带有 ttl(Time-To-Live过期时间) 的消息发送到一个临时队列(DelayQueue),该队列没有消费者;
该消息在DelayQueue中停留直至过期,同时该消息没有ReQueue(重新入队),就变成了死信(Dead-letter或Dead-message),死信自动地被发送给了配置好的DLX(Dead-Letter-Exchange);
DLX根据路由规则把消息路由到了配置好的队列中(DeadLetterQueue),队列中的消息被消费者消费。
maven依赖
引入amqp的依赖, 生产者和消费者都需要
<!--amqp 适用rabbitmq--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
boostrap配置
同样,生产者和消费者都需要
properties.yaml:
spring: rabbitmq: host: localhost port: 5672 username: admin password: 123456
使用配置类创建各组件
rabbitmq的基本的组件是Exchange(交换机)、Queue(队列)、Binding(绑定)。实现延时队列需要定义如下组件:
- 一个带ttl的Queue临时队列;
- 一个普通的用于业务的Queue,即死信队列;
- 定义一个Exchange,即死信交换器DLX(Dead-Letter-Exchange);
- 再定义一个Bingding把死信队列和死信交换器绑定在一起。
import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class DelayQueueConfig { /**为了更贴合业务,参数名不使用DeadQueue之类的*/ /**延迟队列名*/ private static String DELAY_QUEUE = "delay.queue"; /**延迟队列(死信队列)交换器名*/ private static String DELAY_EXCHANGE = "delay.exchange"; /**处理业务的队列(死信队列)*/ private static String PROCESS_QUEUE = "process.queue"; /**ttl(10秒)*/ private static int DELAY_EXPIRATION = 10000; /** * 创建延迟队列 * "x-dead-letter-exchange"参数定义死信队列交换机 * "x-dead-letter-routing-key"定义死信队列中的消息重定向时的routing-key * "x-message-ttl"定义消息的过期时间 */ @Bean public Queue delayQueue(){ return QueueBuilder.durable(DELAY_QUEUE) .withArgument("x-dead-letter-exchange", DELAY_EXCHANGE) .withArgument("x-dead-letter-routing-key", PROCESS_QUEUE) .withArgument("x-message-ttl", DELAY_EXPIRATION) .build(); } /**创建用于业务的队列*/ @Bean public Queue processQueue(){ return QueueBuilder.durable(PROCESS_QUEUE) .build(); } /**创建一个DirectExchange*/ @Bean public DirectExchange delayExchange(){ return new DirectExchange(DELAY_EXCHANGE); } /**绑定Exchange和queue,把消息重定向到业务queue*/ @Bean Binding dlxBinding(DirectExchange directExchange, Queue processQueue){ return BindingBuilder.bind(processQueue) .to(directExchange) .with(PROCESS_QUEUE); //绑定,以PROCESS_QUEUE为routing key } }
生产者创建发送消息的组件
@Component public class MessageSender { @Autowired private AmqpTemplate amqpTemplate; public void send(String routingKey, String msg) { //amqpTemplate.convertAndSend("process.queue", msg); amqpTemplate.convertAndSend(routingKey, msg); } }
消费者自动接收并处理消息的组件
@Component //注意监听的Queue是用于业务的ProcessQueue, 而不是临时存放消息的DelayQueue @RabbitListener(queues = "process.queue") public class MessageReceiver { @RabbitHandler() public void doSth(String msg) { //TODO } }
拓展
该方案存在的缺点:消息会堆积在队列中,如果队列已满,新的消息会变为死信,会直接重发送到死信队列,此时就没有“延迟”的效果。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?