rabbitmq之消息分发策略

本文详细介绍了RabbitMQ的消息分发策略,包括轮询分发和公平分发,并展示了如何在SpringBoot中配置和实现,以及高级特性如消息持久化、死信队列等对系统性能的影响。
摘要由CSDN通过智能技术生成

RabbitMQ之消息分发策略

RabbitMQ 是一种强大且灵活的消息队列系统,被广泛应用于分布式系统和微服务架构中。它能够有效地实现系统解耦、异步处理、削峰填谷等功能。本文将深入探讨 RabbitMQ 中的消息分发策略,主要包括默认的轮训分发和更为智能的公平分发策略,并介绍如何在 Spring Boot 项目中进行配置和实现,确保消息处理的高效性和可靠性。
消息分发策略是消息队列系统的核心功能之一,它直接影响到系统的性能和消息处理的效率。RabbitMQ 提供了多种消息分发策略,允许开发者根据具体需求进行选择和配置。了解并正确配置这些策略,对于构建高效可靠的分布式系统至关重要。

RabbitMQ 消息分发策略

轮训分发(Round-Robin)

轮训分发是 RabbitMQ 的默认消息分发策略。它将消息按顺序均匀地分发给每个消费者,而不考虑每个消费者的处理能力和处理速度。这种策略的优点是实现简单,能够在消费者处理能力均衡的情况下提供较好的性能。然而,在消费者处理能力不均衡的情况下,轮训分发可能导致一些消费者过载,而另一些消费者闲置,降低系统的整体效率。

公平分发(Fair Dispatch)

公平分发是一种更为智能的消息分发策略,它根据每个消费者的处理能力和处理速度来分配消息,确保处理能力强的消费者能够处理更多的消息,从而提高整体系统的效率。公平分发通过配置 prefetch 值来实现,该值定义了 RabbitMQ 在接收消费者的 ack(确认)之前可以发送给该消费者的最大消息数。通过设置较小的 prefetch 值,可以确保每个消费者在处理完当前消息之前不会收到更多的消息,从而实现消息的公平分发。

在 Spring Boot 中配置 RabbitMQ

引入依赖

首先,需要在 Spring Boot 项目中引入 RabbitMQ 相关的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

配置文件

application.properties 文件中进行 RabbitMQ 的相关配置:

# RabbitMQ 连接配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

# 手动确认模式
spring.rabbitmq.listener.simple.acknowledge-mode=manual
# 设置 prefetch 值,开启公平分发
spring.rabbitmq.listener.simple.prefetch=1

上述配置中,spring.rabbitmq.listener.simple.acknowledge-mode=manual 设置为手动确认模式,spring.rabbitmq.listener.simple.prefetch=1 设置为每次只处理一个消息,开启公平分发。

消费者实现

以下是两个消费者的实现示例,分别模拟了处理时间不同的情况,从而演示公平分发策略的效果。

消费者1

消费者1模拟处理时间较短的情况:

@RabbitListener(queues = Constant.ACK_ACK_QUEUE, ackMode = "MANUAL")
@RabbitHandler
public void ackAckReceiver(@NotNull Message message, Channel channel) {
    try {
        byte[] body = message.getBody();
        log.info("basicAck 收到的消息为: " + new String(body));
        int time = 1 * 1000; // 处理时间为1秒
        log.info("开始消费消息:{},耗时{}秒,", DateUtil.Date2LongtString(new Date()), time / 1000);
        Thread.sleep(time);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        // 手动确认消息
        channel.basicAck(deliveryTag, false);
        log.info("basicAck ack完毕 deliveryTag:{},时间:{}:", deliveryTag, DateUtil.Date2LongtString(new Date()));
    } catch (Exception e) {
        log.info("消息签收失败", e);
    }
}
消费者2

消费者2模拟处理时间较长的情况:

@RabbitListener(queues = Constant.ACK_ACK_QUEUE, ackMode = "MANUAL")
@RabbitHandler
public void ackAckReceiver(@NotNull Message message, Channel channel) {
    try {
        byte[] body = message.getBody();
        log.info("basicAck 收到的消息为: " + new String(body));
        int time = 10 * 1000; // 处理时间为10秒
        log.info("开始消费消息:{},耗时{}秒,", DateUtil.Date2LongtString(new Date()), time / 1000);
        Thread.sleep(time);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        // 手动确认消息
        channel.basicAck(deliveryTag, false);
        log.info("basicAck ack完毕 deliveryTag:{},时间:{}:", deliveryTag, DateUtil.Date2LongtString(new Date()));
    } catch (Exception e) {
        log.info("消息签收失败", e);
    }
}

方法级别的 prefetch 配置

虽然全局配置 prefetch 是最常见的方式,但在某些场景下,您可能希望在方法级别设置 prefetch 值。然而,Spring AMQP 目前不支持在注解中直接设置 prefetch 值。如果需要不同的 prefetch 值,可以通过创建不同的 SimpleRabbitListenerContainerFactory 实例来实现。

创建不同的 SimpleRabbitListenerContainerFactory

通过创建不同的 SimpleRabbitListenerContainerFactory 实例,可以为特定的消费者方法设置不同的 prefetch 值:

@Bean
public SimpleRabbitListenerContainerFactory prefetchOneRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    configurer.configure(factory, connectionFactory);
    factory.setPrefetchCount(1);
    return factory;
}

@RabbitListener(queues = Constant.ACK_ACK_QUEUE, ackMode = "MANUAL", containerFactory = "prefetchOneRabbitListenerContainerFactory")
public void ackAckReceiverWithPrefetchOne(@NotNull Message message, Channel channel) {
    // 消费者代码
}

通过以上配置,可以为特定的消费者方法设置不同的 prefetch 值,以实现更加灵活的消息分发策略。

RabbitMQ 高级特性

除了基本的轮训分发和公平分发策略,RabbitMQ 还提供了许多高级特性,帮助开发者构建更加复杂和高效的消息队列系统。

消息持久化

消息持久化是确保消息在 RabbitMQ 崩溃或重启后不会丢失的一种机制。在声明队列和消息时,可以通过设置相关属性来实现消息持久化。

声明持久化队列
@Bean
public Queue durableQueue() {
    return new Queue("durableQueue", true); // 第二个参数为 true 表示队列持久化
}
发送持久化消息
public void sendPersistentMessage(String message) {
    rabbitTemplate.convertAndSend("durableQueue", (Object) message, msg -> {
        msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        return msg;
    });
}

死信队列(DLQ)

死信队列是指那些由于各种原因(如消息被拒绝、消息超时等)未能被处理的消息会被重新投递到一个特殊的队列中,这个队列就是死信队列。配置死信队列可以帮助开发者更好地处理和分析失败的消息。

配置死信队列
@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"); // 死信路由键
    return new Queue("mainQueue", true, false, false, args);
}

@Bean
public Queue dlq() {
    return new Queue("dlq");
}

@Bean
public DirectExchange dlxExchange() {
    return new DirectExchange("dlx-exchange");
}

@Bean
public Binding dlqBinding() {
    return BindingBuilder.bind(dlq()).to(dlxExchange()).with("dlx-routing-key");
}

消息优先级

RabbitMQ 支持基于优先级的消息队列,允许开发者设置不同优先级的消息,以便高优先级的消息能够被优先处理。

配置优先级队列
@Bean
public Queue priorityQueue() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-max-priority", 10); // 设置最大优先级
    return new Queue("priorityQueue", true, false, false, args);
}
发送带优先级的消息
public void sendPriorityMessage(String message, int priority) {
    rabbitTemplate.convertAndSend("priorityQueue", (Object) message, msg -> {
        msg.getMessageProperties().setPriority(priority);
        return msg;
    });
}

延迟队列

延迟队列允许消息在指定时间后再进行处理。RabbitMQ 可以通过插件支持延迟队列功能,也可以通过 TTL(Time-To-Live)和死信队列来实现延迟队列。

配置延迟队列(基于插件)
@Bean
public Queue delayQueue() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    return new Queue("delayQueue", true, false, false, args);
}

@Bean
public CustomExchange delayExchange() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    return new CustomExchange("delayExchange", "x-delayed-message", true, false, args);
}

@Bean
public Binding delayBinding() {
    return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay-routing-key").noargs();
}
发送延迟消息
public void sendDelayedMessage(String message, int delay) {
    rabbitTemplate.convertAndSend("delayExchange", "delay-routing-key", message, msg -> {
        msg.getMessageProperties().setDelay(delay);
        return msg;
    });
}

事务支持和确认机制

RabbitMQ 支持事务和消息确认机制,确保消息的可靠传输和处理。

事务支持

在事务模式下,所有的消息发布和确认都在一个事务中完成。如果事务提交失败,所有操作都会回滚。

public void sendTransactionalMessage(String message) {
    rabbitTemplate.setChannelTransacted(true);
    try {
        rabbitTemplate.convertAndSend("transactionalQueue", message);
        // 模拟异常
        int result = 1 / 0;
        rabbitTemplate.convertAndSend("transactionalQueue", message + " - second part");
    } catch (Exception e) {
        log.error("事务失败,消息回滚", e);
    }
}
确认机制

消息确认机制提供了更高的灵活性,允许消费者在确认处理完消息后再通知 RabbitMQ,确保消息不丢失。

@RabbitListener(queues = "ackQueue", ackMode = "MANUAL")
public void handleAckMessage(@NotNull Message message, Channel channel) {
    try {
        String body = new String(message.getBody());
        log.info("收到消息: " + body);
        // 处理消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
        log.error("处理消息失败", e);
        try {
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        } catch (IOException ioException) {
            log.error("消息拒绝失败", ioException);
        }
    }
}

总结

RabbitMQ 提供了丰富的消息分发策略和高级特性,帮助开发者构建高效、可靠的分布式系统。在实际应用中,选择合适的消息分发策略(如轮训分发和公平分发)以及合理配置高级特性(如消息持久化、死信队列、消息优先级、延迟队列、事务支持和确认机制)可以显著提高系统的性能和稳定性。

通过本文的详细介绍和示例代码,您可以在 Spring Boot 项目中灵活应用 RabbitMQ 的各种功能,优化消息队列的处理流程,确保消息的高效处理和分发。同时,理解和应用这些高级特性,可以帮助您应对复杂的业务场景,提高系统的健壮性和可维护性。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值