RabbitMQ高级(四) - 延迟消息

235 阅读3分钟

延迟消息: 生产者放松消息时指定一个时间,消费者不会立刻受到消息,而是在指定时间之后才收到消息。
延迟任务: 设置在一定时间之后才执行的任务.

死信交换机

我么可以通过死信交换机实现延迟消息。

当一个队列的消息满足下列情况之一时,就会成为死信(dead letter):

  • 消费者使用 basic.reject 或 basic.nack 声明消费失败,并且消息的 requeue 参数设置为 false
  • 消息是一个过期消息(达到了队列或消息本身设置的过期时间),超时无人消费
  • 要投递的队列消息堆积满了,最早的消息可能成为死信

如果队列通过 dead-letter-exchange 属性指定了一个交换机,那么该队列中的死信就会投递到这个交换机中。这个交换机成为死信交换机(Dead Letter Exchange, 简称 DLX)。

下图给 simple.queue 队列绑定了一个死信交换机 image.png

下图我定义了两个队列和两个交换机,simple.direct 绑定 simple.queue(指定了死信交换机,见上图), dix.direct 绑定了dix.queue,我们再给dix.queue绑定一个消费者。不要让 simple.queue 绑定消费者,不然无法测试出死信。

image.png

image.png

@RabbitListener(queues = "dix.queue")
public void onMessage(String message) {
    log.info("receive message: {}", message);
}

发送消息的代码

@Test
public void testSendTTLMessage(){
    Message message = MessageBuilder
            .withBody("hello".getBytes(StandardCharsets.UTF_8))
            .setExpiration("3000")//设置过期时间 单位 ms
            .build();
    rabbitTemplate.convertAndSend("simple.direct","hi",message);
    log.info("消息发送成功");
}

延迟消息插件

RabbitMQ 的官方也推出了一个插件,原生支持延迟消息功能。该插件的原理是设计了一种支持延迟消息功能的交换机,当消息投递到交换机可以暂存一定时间,到期后再投递到队列。

官方文档:

Scheduling Messages with RabbitMQ | RabbitMQ

1.下载插件

插件下载地址:

Releases · rabbitmq/rabbitmq-delayed-message-exchange (github.com)

下载对应版本的插件:

image.png

然后复制到RabbitMQ的plugins目录中。使用命令启用插件:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

docker 命令:

docker exec -it 容器名/容器id rabbitmq-plugins enable rabbitmq_delayed_message_exchange

基于注解方式

在consumer服务基于@RabbitListener注解来声明队列、交换机和绑定队列和交换机,并且设置交换机为延迟交换机:

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "delay.queue", durable = "true"),
            exchange = @Exchange(value = "delay.direct", delayed = "true"),
            key = "delay"
    ))
    public void listenDelayQueue(String msg) {
        log.info("delay.queue:" + msg);
    }
delayed = "true"设置交换机为延迟交换机

基于@Bean方式

在consumer服务基于@Bean注解来声明交换机、队列和绑定队列和交换机,并且设置交换机为延迟交换机:

@Configuration
public class DirectConfiguration {

    @Bean
    public DirectExchange delayExchange() {
        return ExchangeBuilder
                .directExchange("delay.direct")
                .delayed() // 设置为延迟交换机
                .durable(true)
                .build();
    }

    @Bean
    public Queue delayedQueue() {
        return new Queue("delay.queue");
    }

    @Bean
    public Binding delayQueueBinding() {
        return BindingBuilder.bind(delayedQueue()).to(delayExchange()).with("delay");
    }
}
.delayed()设置交换机为延迟交换机

延迟消息发送

在publisher服务中的测试类添加一个测试方法,通过消息头x-delay来设置过期时间,实现延迟消息发送:

    @Test
    void testSendDelayMessage() {
        rabbitTemplate.convertAndSend("delay.direct", "delay", "hello", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setDelay(10000);
                return message;
            }
        });
        log.info("消息发送成功");
    }
.setDelay()通过消息头x-delay来设置消息的延迟时间