java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > RabbitMQ队头阻塞

简单聊聊RabbitMQ中的队头阻塞问题

作者:Java中文社群

这篇文章主要为大家详细介绍了RabbitMQ中的队头阻塞问题以及相关的解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

RabbitMQ 延迟消息的队头阻塞问题是指,在使用死信队列(DLX)和 TTL(消息过期时间)实现延迟消息时,由于队列的先进先出(FIFO)特性,在队列头部消息未过期的情况下,即使后续消息已经过期也不能及时处理的情况

实现原理

RabbitMQ 延迟消息的实现方式有以下两种:

而我们本文要讨论的“RabbitMQ 延迟消息的队头阻塞问题”只会发生在死信队列+TTL 的实现方式中。

死信队列 + TTL 的实现流程如下:

代码实现

死信队列 + TTL 在 Spring Boot 项目中的实现代码如下。

定义死信交换器(DLX)和死信队列

// Spring Boot 配置示例
@Configuration
public class RabbitConfig {
    // 定义死信交换器
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange("dlx.exchange");
    }

    // 定义死信队列
    @Bean
    public Queue dlxQueue() {
        return new Queue("dlx.queue");
    }

    // 绑定死信队列到 DLX
    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("dlx.routing.key");
    }

    // 定义普通队列,设置死信交换器和路由键
    @Bean
    public Queue mainQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", "dlx.exchange");
        args.put("x-dead-letter-routing-key", "dlx.routing.key");
        // 可选:设置队列级别的 TTL(所有消息统一过期时间)
        args.put("x-message-ttl", 10000); // 10秒
        return new Queue("main.queue", true, false, false, args);
    }

    // 主队列绑定到默认交换器(根据需要调整)
    @Bean
    public Binding mainBinding() {
        return BindingBuilder.bind(mainQueue()).to(new DirectExchange("default.exchange")).with("main.routing.key");
    }
}

发送消息时设置 TTL(消息级别)

// 发送延迟消息(消息级别 TTL)
public void sendDelayedMessage(String message, int delayMs) {
    rabbitTemplate.convertAndSend("default.exchange", "main.routing.key", message, msg -> {
        // 设置消息过期时间(覆盖队列级别的 TTL)
        msg.getMessageProperties().setExpiration(String.valueOf(delayMs));
        return msg;
    });
}

消费者监听死信队列

@RabbitListener(queues = "dlx.queue")
public void handleDelayedMessage(String message) {
System.out.println("处理延迟消息: " + message);
}

所以说消息的过期时间 TTL 的设置方式有以下两种:

队列级别:通过设置队列的 x-message-ttl 参数,设置队列统一的过期时间。

Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000); // 设置队列消息过期时间为 60 秒
channel.queueDeclare(queueName, true, false, false, args);

消息级别:通过给每个消息设置 expiration 属性,为每个消息设置过期时间。

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
        .deliveryMode(2) // 消息持久化
        .expiration("60000") // 设置消息过期时间为 60 秒
        .build();
channel.basicPublish(exchangeName, routingKey, properties, message.getBytes());

如果同时设置了消息级 TTL 和队列级 TTL,消息的实际过期时间会取两者中的最小值。

造成队头阻塞的原因

造成队头阻塞的原因有以下两个:

如下图所示:

解决方案

小结

队头阻塞问题是发生在使用死信队列加 TTL 实现 RabbitMQ 延迟消息的场景中,造成的原因是队列先进先出的特性,加上延迟消息的检查机制导致的,我们可以使用 RabbitMQ 的延迟插件来避免此问题。

那么问题来了,使用延迟插件如何实现延迟任务?它和死信队列的实现方式有哪些具体的区别呢?

到此这篇关于简单聊聊RabbitMQ中的队头阻塞问题的文章就介绍到这了,更多相关RabbitMQ队头阻塞内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文