Rabbitmq简单实现延迟消费

本文介绍如何使用RabbitMQ的死信队列特性实现消息的延迟消费,通过设置消息的TTL(Time To Live)来达到延迟效果,适用于如支付超时取消订单等场景。

TTL实现方式

背景:

很多时候我们用rabbitmq作为消息中间件时,不希望消息立马被消费,而是希望在一定时间后消费,比如支付超时取消订单,30分钟之内没有支付就会取消订单,取消订单可以认为是一个消费动作(当然可以用定时任务实现超时取消订单),类似这种场景下我们需要设计延迟消费的逻辑。
目前相关mq本身不带有延迟消费的功能,需要借助一些特性进行实现,以下是rabbitmq的根据设置ttl的方式实现,即死信队列实现延迟队列

方式

  1. 生产者将需要延迟消费的消息发送到一个临时队列Q1,并且设置消息存活时间,即为延迟消费时间
  2. 消息死亡后会成为死信,再为Q1队列设置死信交换机、死信路由,转发到真正消费的队列Q2

代码实现

启动类配置相关bean

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.HashMap;
import java.util.Map;


/**
 * @Description 启动类
 * @Author cdbMeng
 * @Date 2020/4/306:52 下午
 * @Version$ 1.0
 **/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SamplesApp implements WebMvcConfigurer {

    /**
     * 真正消费队列Q2
     * @return
     */
    @Bean
    Queue msgQueue() {
        return new Queue("Q2");
    }

    /**
     * 创建交换机
     * @return
     */
    @Bean
    DirectExchange directExchange() {
        return new DirectExchange("exchange");
    }

    /**
     * 交换机绑定Q1
     * @return
     */
    @Bean
    public Binding bindCacheQueue() {
        return BindingBuilder.bind(putShipCacheQueue()).to(directExchange()).with("Q1");
    }

    /**
     * 交换机绑定Q2
     * @return
     */
    @Bean
    public Binding bindRealQueue() {
        return BindingBuilder.bind(msgQueue()).to(directExchange()).with("Q2");
    }

    /**
     * 配置死信对列
     * @return
     */
    @Bean
    Queue putShipCacheQueue() {
        Map<String, Object> map = new HashMap<>();
        //配置队列过期时间,队列里的所有消息存活时间,如果不设置则可以设置每条消息的存活时间,如果两者都设置了,则以小的那个为准
        map.put("x-message-ttl", 10000);
        //绑定到一个交换机
        map.put("x-dead-letter-exchange", "exchange");
        //路由到真正消费的对列Q2
        map.put("x-dead-letter-routing-key", "Q2");
        return new Queue("Q1", true, false, false, map);
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext run = new SpringApplication(SamplesApp.class).run(args);
    }
}

生产者

/**
 * @Description 生产者
 * @Author cdbMeng
 * @Date 2020/5/910:53 上午
 * @Version$ 1.0
 **/
@Component
@Slf4j
public class MqProducer {

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 发送消息,
     *
     * @param msg
     */
    public void sendDelayMsg(String msg) {
        System.out.println("cache-queue 发送时间:" + System.currentTimeMillis() + ",msg:" + msg );
        rabbitTemplate.convertAndSend("exchange","Q1",  msg);
    }
    
    /**
     * 设置消息有效期时间
     *
     * @param msg
     */
    public void sendDelayMsg(String msg, Long delayTime) {
        System.out.println("cache-queue 发送时间:" + System.currentTimeMillis() + ",msg:" + msg + ",过期时间:" + delayTime);

        MessagePostProcessor postProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration(delayTime + "");
                return message;
            }
        };
        rabbitTemplate.convertAndSend("Q1", (Object) msg, postProcessor);
    }
}

消费者

@Component
public class Consumer {
    
    @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "Q2", durable = "true"),
            exchange = @Exchange(value = "exchange"),
            key = "msg.*"))
    @RabbitHandler
    public void consumeDelayMsg(@Payload String msg, @Headers Map<String, Object> headers, Channel channel) throws IOException {
        //消费者消费
        System.out.println("repeat msg is :" + msg + ",收到消息时间:" + System.currentTimeMillis());

        //手动签收
        channel.basicAck((Long) headers.get(AmqpHeaders.DELIVERY_TAG), false);
    }
}

测试结果

  • 生产者
  • 在这里插入图片描述
  • 消费者
  • 在这里插入图片描述
### Spring Boot 中通过 RabbitMQ 插件实现延迟队列 在 Spring Boot 应用程序中,可以利用 RabbitMQ 提供的 `rabbitmq_delayed_message_exchange` 插件来实现延迟队列功能。以下是具体实现方法: #### 1. 安装 RabbitMQ 延迟插件 首先需要下载并启用 RabbitMQ延迟消息交换插件。将插件文件放置到 RabbitMQ 的插件目录下,并执行以下命令完成安装: ```bash rabbitmq-plugins enable rabbitmq_delayed_message_exchange ``` 此操作会激活支持延迟消息处理的功能[^1]。 #### 2. 配置 RabbitMQ Exchange 和 Queue 为了实现延迟队列,在 RabbitMQ 中需定义一个特殊的 Exchange 类型为 `x-delayed-message` 并绑定对应的 Queue。这种类型的 Exchange 支持基于每条消息设置不同的延迟时间。 ##### (a) 创建 Delayed Message Exchange 在应用程序启动时初始化该 Exchange 及其参数: ```java @Configuration public class RabbitConfig { @Bean public CustomExchange delayedMessageExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed.message.exchange", "x-delayed-message", true, false, args); } } ``` 此处自定义了一个名为 `"delayed.message.exchange"` 的 Exchange,类型设为 `"x-delayed-message"`,并通过参数指定了底层的实际路由模式为 Direct。 #### 3. 发送带有延迟的消息 当向队列发送消息时,可通过 AMQP 属性中的 header 设置延迟毫秒数字段 (`x-delay`) 来控制消息何时被投递给消费者。 下面是一个简单的生产者示例代码片段: ```java @Component public class DelayedMessageSender { private final AmqpTemplate amqpTemplate; public DelayedMessageSender(AmqpTemplate amqpTemplate) { this.amqpTemplate = amqpTemplate; } public void sendDelayedMessage(String message, int delayMillis) { MessageProperties properties = new MessageProperties(); properties.setHeader("x-delay", delayMillis); Message msg = new Message(message.getBytes(), properties); amqpTemplate.convertAndSend("delayed.message.exchange", "", msg); } } ``` 这里设置了消息头 `x-delay` 表明当前消息应等待多久才允许传递给订阅方[^2]。 #### 4. 接收端逻辑编写 最后一步是在监听器类里处理接收到的数据包。假设目标队列为默认绑定至上述 exchange,则只需正常声明 listener 即可捕获到期后的数据流。 ```java @Component public class DelayedMessageListener { @RabbitListener(queues = "#{queueName}") public void handleMessage(String message) throws InterruptedException { System.out.println("Received delayed message: " + message); } } ``` 以上即完成了整个流程的设计与编码工作[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值