RabbitMQ延迟队列完全指南:TTL+死信与插件双方案详解 RabbitMQ延迟队列完全指南TTL死信与插件双方案详解1. 什么是延迟队列2. 方案对比概览3. 方案一TTL 死信交换机DLX3.1 核心概念3.2 实现流程图3.3 代码实现Spring Boot3.4 进阶可变延迟时间4. 方案二rabbitmq_delayed_message_exchange插件4.1 插件安装4.2 实现流程图4.3 代码实现4.4 插件方案注意事项5. 两种方案选择建议6. 常见问题Q1队列TTL和消息TTL同时设置以哪个为准Q2如何实现不同延迟时间的消息Q3消息成为死信后会保留原始消息头吗The Begin点点关注收藏不迷路在电商系统中“用户下单30分钟未支付自动取消”这类需求非常常见。延迟队列正是解决此类定时任务的最佳方案。本文将详细介绍RabbitMQ延迟队列的两种实现方式。1. 什么是延迟队列延迟队列指消息被发送后不立即投递给消费者而是等待指定时间后才被消费。典型应用场景订单超时自动取消、关闭支付成功15分钟后发送评价提醒用户注册3天后发放优惠券2. 方案对比概览对比维度TTL 死信队列延迟插件实现复杂度较复杂需配置多队列简单一个交换机搞定延迟精度一般依赖队列头部检测较高灵活度不同延迟需不同队列每条消息可单独设置延迟性能一般高但官方指出不适用于海量延迟消息版本要求任意版本需安装插件3. 方案一TTL 死信交换机DLX这是RabbitMQ原生支持的方式利用消息存活时间和死信交换机两个特性实现。3.1 核心概念TTL消息存活时间。可设置队列TTL队列中所有消息统一过期时间或消息TTL每条消息单独设置以时间短的为准。死信交换机DLX消息成为死信后会被投递到指定交换机。消息成为死信有三种情况消息被消费者拒绝basic.reject/basic.nack且requeuefalse消息过期TTL到期队列达到最大长度3.2 实现流程图实际消费区等待区发送消息路由消息过期路由生产者业务交换机业务队列设置TTLDLX死信交换机死信队列消费者3.3 代码实现Spring BootStep 1声明业务队列带TTL和DLXConfigurationpublicclassDelayQueueConfig{publicstaticfinalStringBUSINESS_EXCHANGEbusiness.exchange;publicstaticfinalStringBUSINESS_QUEUEbusiness.queue;publicstaticfinalStringDEAD_EXCHANGEdead.exchange;publicstaticfinalStringDEAD_QUEUEdead.queue;publicstaticfinalStringROUTING_KEYorder.timeout;// 1. 业务交换机BeanpublicDirectExchangebusinessExchange(){returnnewDirectExchange(BUSINESS_EXCHANGE);}// 2. 死信交换机BeanpublicDirectExchangedeadExchange(){returnnewDirectExchange(DEAD_EXCHANGE);}// 3. 业务队列设置TTL和死信交换机BeanpublicQueuebusinessQueue(){returnQueueBuilder.durable(BUSINESS_QUEUE).ttl(30000)// 30秒过期.deadLetterExchange(DEAD_EXCHANGE)// 指定死信交换机.deadLetterRoutingKey(ROUTING_KEY)// 死信路由键.build();}// 4. 死信队列BeanpublicQueuedeadQueue(){returnQueueBuilder.durable(DEAD_QUEUE).build();}// 5. 绑定BeanpublicBindingbindBusiness(){returnBindingBuilder.bind(businessQueue()).to(businessExchange()).with(ROUTING_KEY);}BeanpublicBindingbindDead(){returnBindingBuilder.bind(deadQueue()).to(deadExchange()).with(ROUTING_KEY);}}Step 2发送消息ServicepublicclassOrderService{AutowiredprivateRabbitTemplaterabbitTemplate;publicvoidcreateOrder(StringorderId){// 业务处理...// 发送延迟消息30分钟后检查订单状态rabbitTemplate.convertAndSend(DelayQueueConfig.BUSINESS_EXCHANGE,DelayQueueConfig.ROUTING_KEY,orderId);}}Step 3消费延迟消息ComponentSlf4jpublicclassDelayMessageConsumer{RabbitListener(queuesDelayQueueConfig.DEAD_QUEUE)publicvoidhandleOrderTimeout(StringorderId){log.info(收到订单超时检查消息订单号{},orderId);// 查询订单状态若未支付则取消订单cancelOrderIfNotPaid(orderId);}}3.4 进阶可变延迟时间不同场景需要不同延迟时间可通过消息级TTL实现。// 发送消息时单独设置过期时间publicvoidsendDelayedMessage(Stringmessage,intdelaySeconds){MessagePropertiespropsnewMessageProperties();props.setExpiration(String.valueOf(delaySeconds*1000));MessagemsgMessageBuilder.withBody(message.getBytes()).andProperties(props).build();rabbitTemplate.convertAndSend(BUSINESS_EXCHANGE,ROUTING_KEY,msg);}注意RabbitMQ TTL采用队列头部检测机制——只有队列头部的消息过期才会被移除。若前一条消息未过期后一条消息即使已过期也无法成为死信。4. 方案二rabbitmq_delayed_message_exchange插件官方推荐的延迟插件使用更简单优雅。4.1 插件安装# 查看Erlang版本erl-version# 下载对应版本插件wgethttps://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/v3.12.0/rabbitmq_delayed_message_exchange-3.12.0.ez# 移动到插件目录mvrabbitmq_delayed_message_exchange-3.12.0.ez /usr/lib/rabbitmq/plugins/# 启用插件rabbitmq-pluginsenablerabbitmq_delayed_message_exchange# 重启RabbitMQsystemctl restart rabbitmq-server4.2 实现流程图x-delay: 30秒存储消息30秒到期路由生产者延迟交换机x-delayed-messageMnesia存储普通队列消费者4.3 代码实现Step 1声明延迟交换机ConfigurationpublicclassDelayedPluginConfig{BeanpublicCustomExchangedelayedExchange(){MapString,ObjectargsnewHashMap();args.put(x-delayed-type,direct);returnnewCustomExchange(delayed.exchange,x-delayed-message,true,// 持久化false,// 自动删除args);}BeanpublicQueuedelayedQueue(){returnQueueBuilder.durable(delayed.queue).build();}BeanpublicBindingdelayedBinding(){returnBindingBuilder.bind(delayedQueue()).to(delayedExchange()).with(delayed.key).noargs();}}Step 2发送消息publicvoidsendDelayedMessage(Stringmessage,intdelayMillis){MessagePropertiespropsnewMessageProperties();props.setHeader(x-delay,delayMillis);// 设置延迟时间MessagemsgMessageBuilder.withBody(message.getBytes()).andProperties(props).build();rabbitTemplate.convertAndSend(delayed.exchange,delayed.key,msg);}4.4 插件方案注意事项插件的设计不适用于大量延迟消息数十万甚至数百万条未调度消息的场景生产环境请谨慎评估消息量级。主要限制延迟消息在每个节点只有一个持久化副本节点宕机可能导致消息丢失延迟交换机不支持mandatory参数无法感知无法路由的消息5. 两种方案选择建议海量一般固定延迟每条消息不同允许不允许需要延迟队列延迟消息量大吗TTL死信方案延迟时间灵活吗运维允许装插件吗插件方案优点稳定可靠缺点不同延迟需多队列优点灵活易用缺点海量场景有风险6. 常见问题Q1队列TTL和消息TTL同时设置以哪个为准以时间短的为准。例如队列TTL30秒消息TTL20秒消息会在20秒后过期。Q2如何实现不同延迟时间的消息插件方案每条消息单独设置x-delay即可TTLDLX方案为不同延迟时间创建不同业务队列分别设置TTLQ3消息成为死信后会保留原始消息头吗会保留。原始消息的headers、properties等信息会一并投递到死信队列。一句话总结小规模灵活场景用插件大规模生产环境用TTL死信固定延迟用队列TTL动态延迟用消息TTL或插件。The End点点关注收藏不迷路