rabbitmq消息接收的可靠性机制

消费端的两种处理机制:

两种机制的区别, 第一种是在消费端出现异常, 系统执行的, 如果多次重试失败, 则可以抛出指定异常拒绝该消息(等同与reject)或者将消息发送到指定队列;

第二种ack机制必须要内部catch住消费者的异常, 手动的进行ack或者nack给rabbitmq , 然后rabbitmq根据配置重新发送消息或者直接舍弃该消息

1. spring.rabbitmq.listener.retry配置的重发是在消费端应用内处理的,不是rabbitqq重发. 因为retry是消费端内部处理的,包括异常也是内部处理,对于rabbitmq是不知道的

2.消息ack

a. 返回ack则表明消息被处理, rabbitmq删除消息,

b. nack或者reject并且requeue=true则rabbitmq重新发送该消息,requeue=false则讲消息丢弃或放入私信队列

    nack和reject区别, 前者可以批量拒绝 消息, reject只能单条消息拒绝

c. 如果rabbitmq发出的消息一直得不到服务端的相应, 则在

         rabbitmq断开,连接后会自动重新推送(不管是网络问题还是宕机)

         消费端应用重启,消息会自动重新推送

 

关于retry

spring.rabbitmq.listener.simple.retry.max-attempts=5 最大重试次数
 spring.rabbitmq.listener.simple.retry.enabled=true 是否开启消费者重试(为false时关闭消费者重试,这时消费端代码异常会一直重复收到消息) 
spring.rabbitmq.listener.simple.retry.initial-interval=5000 重试间隔时间(单位毫秒) 
???? spring.rabbitmq.listener.simple.default-requeue-rejected=false 重试次数超过上面的设置之后是否丢弃(false不丢弃时需要写相应代码将该消息加入死信队列

如果消费一端的重试机制没有被启动, 而且Listener抛出异常的话, 那么该消息就会无限地被重试. 通常我们的做法是抛出AmqpRejectAndDontRequeueException以reject该消息, 同时如果有dead-letter queue被设置的话该消息就会被置入, 否则被丢弃

此外还可以配置MessageRecoverer对异常消息进行处理,此处理会在listener.retry次数尝试完并还是抛出异常的情况下才会调用,默认有两个实现:

@Bean
public MessageRecoverer messageRecoverer(RabbitTemplate rabbitTemplate){
    return new RepublishMessageRecoverer(rabbitTemplate, "exchangemsxferror", "routingkeymsxferror");
}
public class RejectAndDontRequeueRecoverer implements MessageRecoverer {

    protected Log logger = LogFactory.getLog(RejectAndDontRequeueRecoverer.class);
    
    @Override
    public void recover(Message message, Throwable cause) {
        if (this.logger.isWarnEnabled()) {
            this.logger.warn("Retries exhausted for message " + message, cause);
        }
        throw new ListenerExecutionFailedException("Retry Policy Exhausted", new AmqpRejectAndDontRequeueException(cause), message);
    }

}
  • RepublishMessageRecoverer:将消息重新发送到指定队列,需手动配置
  • RejectAndDontRequeueRecoverer:如果不手动配置MessageRecoverer,会默认使用这个,实现仅仅是将异常打印抛出

两种方式在生产中的使用

第一种: 开启手动ack, 然后内部catch后直接处理,然后使用channel对消息进行确认(因为内部catch, 所以retry是一直无效的)

第二种: 不做内部catch, 直接使用retry尝试的次数达到后, 进行的操作来处理

 

测试1

首先配置

spring.rabbitmq.listener.simple.acknowledge-mode=manual
@RabbitListener(queues = "queue.item.search.test")
    public void DocTest(org.springframework.amqp.core.Message message, Channel channel) {
        System.out.println("****************************");
        try {
            System.out.println(message.getBody());
            if (true)
                throw new YuncaiException("");
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

        }catch (Exception e){
            try {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

    }

//第二个参数,false只确认当前一个消息收到,true确认所有consumer获得的消息

channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

//第三个参数, false为直接抛弃, true为rabbitmq重新发送

channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);

//第二个参数,第三个参数, false为直接抛弃, true为rabbitmq重新发送

channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);

 

测试结果:

1. 当消费端收到消息,但没有ack, web控制台显示unack数为1, 当消费端重启后该消息再次被rabbitmq发送给消费端

2. 当nack时, 设置为false, 则该消息直接舍弃, 其实和ack后的结果是一样的

3. 当nack时, 设置为true, 该消息会一直被rabbitmq发送, 死循环

 

测试二

首先配置

spring.rabbitmq.listener.simple.retry.enabled=true
spring.rabbitmq.listener.simple.retry.max-attempts=3
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@RabbitListener(queues = "queue.item.search.test")
    public void DocTest(org.springframework.amqp.core.Message message, Channel channel) {
        System.out.println("****************************");

            System.out.println(message.getBody());
            if (true)
                throw new YuncaiException("");
        try {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

rabbitmq只做一次消息发送, 消费者内部自动重发了两次(1+2=3次), 之后消息停止发送, rabbitmq的web控制台unack数为1

 

关于springamqp做的autoAck(默认)

在当前线程结束没有抛出异常的情况下才会ack, 抛出异常则执行retry机制

 

总结:

如果使用springamqp默认的autoack,并且配置类retry次数, 则在抛出异常重试指定次数后消息被丢弃,相当于消费端发送了ack信号;

如果使用manualack,并且配置了retry次数, 在ack前抛出异常, 则在重试指定次数后, 该消息在broker中还是unack状态;

如果使用manualack,不管配置配置了retry与否, 包住所有可能的异常, 手动ack或者nack,决定broker是ack还是unack;

 

以上三种情况在接受消息的过程中, 消费者和broker连接中断((消费者宕掉),消息都会重新发送


 

RabbitMQ消息可靠性是指确保消息可以安全、可靠地传递到消费者。为了实现消息可靠性RabbitMQ提供了以下机制: 1. 持久化队列:通过将队列设置为持久化,即使RabbitMQ服务器重启,队列中的消息也不会丢失。 2. 持久化消息:将消息设置为持久化,使得即使在RabbitMQ服务器重启前,消息也会被存储在磁盘上。 3. 消息确认机制:生产者可以通过消息确认机制来确保消息已经被成功发送到RabbitMQ中。当消息成功地被RabbitMQ接收到后,生产者会收到一个确认信号。如果RabbitMQ在处理消息时发生错误,生产者可以根据确认信号来重新发送消息。 尽管RabbitMQ提供了上述机制,但仍然存在一些情况下消息可能丢失的风险。例如,如果消息RabbitMQ服务器接收到但尚未持久化到磁盘上时,RabbitMQ服务器崩溃,这可能导致部分消息的丢失。 为了进一步提高消息可靠性,可以采取以下措施: 1. 使用事务:使用事务可以确保消息的原子性提交,即要么全部成功发送,要么全部失败回滚。但是,使用事务会降低RabbitMQ的性能。 2. 设置消息确认模式:可以将消息确认模式设置为"confirm",使得RabbitMQ在收到消息后立即发送确认信号给生产者。 3. 设置备份队列:备份队列可以在主队列发生故障时,将消息转发到备份队列,从而避免消息的丢失。 4. 通过持久化到数据库或其他存储系统来保存重要的消息数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值