RabbitMQ:消息确认

目录

消息确认

消息确认机制

手动确认

代码实现

NONE

AUTO

MANUAL


消息确认

消息确认机制

和发布确认不同,发布确认是生产者和Broker之间的,由Broker向生产者发布一个确认信息,消息确认是消费者向broker发布一个确认信息。

生产者向broker发送完消息后,可能会有两种情况,一种是消息处理成功,一种是消息处理异常。但是RabbitMQ向消费者发送完消息后,就会把这条消息丢弃,如果消费者那边处理异常了就会造成这条消息的丢失。所以为了保证消息的可靠性RabbitMQ提供了消息确认机制。

确认方式分为手动和自动,消费者订阅队列时,就可以指定autoAck参数来进行选择

  • 自动确认:当autoAck等于true时,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正地消费到了这些消息。自动确认模式适合对于消息可靠性要求不高的场景。
  • 手动确认:当autoAck等于false时,RabbitMQ会等待消费者显式地调用Basic.Ack命令,回复确认信号后才从内存(或者磁盘)中移去消息。这种模式适合对消息可靠性要求比较高的场景。

自动确认,当消费者拿到消息后就会自动向RabbitMQ发送确认消息,就好像是有些聊天软件的已读未读功能,当用户点进去后信息就会立刻显示已读,不管用户是否真的已读,所以这种方式的可靠性要相对差一点。

手动确认时可以将队列中的信息分为两个部分,一时等待发送给消费者的,二是以及发送,正在等待确认的,如果RabbitMQ一直没有收到确认信号,并且消费这条消息的消费者已经断开链接了,那么RabbitMQ就会重新安排这条消息入队列,等待下一个消费者。

手动确认

RabbitMQ提供了三种手动确认方法,并且这些确认方法都是与其对应的Channel(通道)相关,换言之消息确认是在同一个Channel内进行。

  • 肯定确认: Channel.basicAck(long deliveryTag,boolean multiple)

当RabbitMQ 收到肯定确认后就会认为该条消息被成功处理了,可以将其丢掉了。

**deliveryTag:**消息的唯一标识,它是一个单调递增的64位长整型值。deliveryTag 是每个通道(Channel)独立维护的,所以在每个通道上都是唯一的,而且不同的通道deliveryTag可以有重复。当消费者确认(ack)一条消息时,必须使用对应的通道进行确认。

**multiple:**是否批量确认。在某些情况下,为了减少网络流量,可以对一系列连续的 deliveryTag 进行批量确认。值为 true 则会一次性 ack 所有小于或等于指定 deliveryTag 的消息。值为 false,则只确认当前指定 deliveryTag 的消息。

批量确认:假如开启批量确认,当前回复的deliveryTag是10的话,那么就会一次性将10之前的没有确认的消息一起回复,如果不开启的话就是只回复当前消息

这里的deliveryTag和multiple,跟发布确认里的一样

  • 否定确认: Channel.basicReject(long deliveryTag,boolean requeue)

当RabbitMQ收到否定确认后表示客户端拒绝了这条消息

**deliveryTag:**和刚刚的一样

**requeue:**表示拒绝后,这条消息如何处理。如果 requeue 参数设置为 true,则 RabbitMQ 会重新将这条消息存入队列,以便可以发送给下一个订阅的消费者。如果 requeue 参数设置为 false,则 RabbitMQ 会把消息从队列中移除,而不会把它发送给新的消费者。

  • 否定确认: Channel.basicNack(long deliveryTag,boolean multiple,boolean requeue)

刚刚那条命令一次只能拒绝一条消息,而Channel.basicNack可以进行批量拒绝,使用方法就是前两个方法的结合

在RabbitMQ的管理界面里也可以看到,这里的get message其实也可以看作是一个消费者,从队列中拿取数据,自上而下分别是,不确认拿完后放回队列,自动确认拿完后不放会队列,拒绝并重新入队,拒绝但不重新入队

代码实现

以上方法是RabbitMQ本身的方法,而这里使用的是Spring来做实现,Spring提供了三种确认策略,和上面的方法并不是一一对应,而是进行了封装。

AcknowledgeMode.NONE

在这种模式下,消息一旦投递给消费者,不管消费者是否成功处理了消息,RabbitMQ 就会自动确认消息,并从队列中移除。如果消费者处理失败,消息可能丢失。这个可以对应上面的自动确认

AcknowledgeMode.AUTO(默认)

在这种模式下,消费者处理成功时会自动确认消息;如果处理过程中抛出异常,则不会确认消息。这个是介于自动确认和手动确认之间。

AcknowledgeMode.MANUAL

在手动确认模式下,消费者必须在成功处理消息后显式调用 basicAck 方法来确认消息。如果消息未被确认,RabbitMQ 会认为消息尚未成功处理,并在消费者可用时重新投递。该模式提高了可靠性:即使消费者处理失败,消息也不会丢失,而是可以被重新处理。

Constants

java 复制代码
public class Constants {
    public final static String ACK_QUEUE = "ack.queue";
    public final static String ACK_EXCHANGE = "ack.exchange";
}

Producer

java 复制代码
@RestController
@RequestMapping("producer")
public class ProducerController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @RequestMapping("create1")
    public String create1(){
        rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack","hello ACK");
        return "发送成功";
    }
}

Consumer

java 复制代码
@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handMessage(Message message, Channel channel ) throws IOException {

            System.out.printf("收到的消息:%s\t  deliveryTag: %d\n",new String(message.getBody(),"UTF-8")
                    ,message.getMessageProperties().getDeliveryTag());
            //执行业务逻辑。。。
            //手动制造异常
            //int i = 3/0;
            //肯定确认
 
    }
}

Config

java 复制代码
@Configuration
public class RabbitMQConfig {
    //声明队列
    @Bean(Constants.ACK_QUEUE)
    public Queue ackQueue(){
        return QueueBuilder.durable(Constants.ACK_QUEUE).build();
    }
    //声明交换机
    @Bean(Constants.ACK_EXCHANGE)
    public DirectExchange directExchange(){
        return ExchangeBuilder.directExchange(Constants.ACK_EXCHANGE).durable(true).build();
    }
    //绑定队列交换机
    @Bean
    public Binding BindingExchange(@Qualifier(Constants.ACK_EXCHANGE) DirectExchange directExchange,
                                   @Qualifier(Constants.ACK_QUEUE) Queue queue){
        return BindingBuilder.bind(queue).to(directExchange).with("ack");
    }
}

yml

XML 复制代码
spring:
  application:
    name: rabbitmq-spring
  rabbitmq:
    addresses: amqp://admin:admin@60.205.230.134:5672/extension
    listener:
      simple:
        acknowledge-mode: NONE
       

NONE

代码运行

可以消费正常信息也被删除,现在我们手动制造一个异常

看以看到消息依然会被删除

使用NONE策略在

  • 消费者正常处理:MQ删除相应消息
  • 消费者异常处理:MQ删除相应消息

AUTO

修改配置信息acknowledge-mode: AUTO

正常处理

异常处理

可以看到消息很快来到了第247条,这是因为消息处理异常后会不停的重复确认

使用NONE策略在

  • 消费者正常处理:消息自动确认
  • 消费者异常处理:消息会不停确认

MANUAL

MANUAL需要自己手动确认调用相应方法,通过try-catch对代码进行异常捕获,如果没有异常就执行肯定确认,否则执行否定

java 复制代码
@Component
public class AckListener {
    @RabbitListener(queues = Constants.ACK_QUEUE)
    public void handMessage(Message message, Channel channel ) throws IOException {

        try {
            System.out.printf("收到的消息:%s\t  deliveryTag: %d\n",new String(message.getBody(),"UTF-8")
                    ,message.getMessageProperties().getDeliveryTag());
            //执行业务逻辑。。。
            //手动制造异常
            //int i = 3/0;
            //肯定确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            //否定确认
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);

        }
    }
}

异常处理

异常处理的现象时,不断的重复从消息队列中拿取数据,这是因为这里的否定确认,选择了处理异常消息会自动重新入队,所以就会陷入一个发送消息->消息处理异常->消息重新入队队->发送消息的循环

//这里两条是上一次演示里的那条消息没有被清除

相关推荐
草莓熊Lotso2 小时前
C++ STL map 系列全方位解析:从基础使用到实战进阶
java·开发语言·c++·人工智能·经验分享·网络协议·everything
shura10142 小时前
如何优雅地实现参数校验
java·开发语言
spencer_tseng3 小时前
Eclipse Oxygen 4.7.2 ADT(android developer tools) Plugin
android·java·eclipse
来来走走4 小时前
Android开发(Kotlin) 协程
android·java·kotlin
河铃旅鹿5 小时前
Android开发-java版:Framgent
android·java·笔记·学习
y***61316 小时前
【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目
java·spring boot·spring
tanxinji6 小时前
RabbitMQ四种交换器类型详解及示例
java·rabbitmq
刘一说6 小时前
一次生产环境 Tomcat 7 + JDK 7 应用启动失败的完整排查与修复实录
java·tomcat·firefox
七夜zippoe7 小时前
JVM类加载机制(Class Loading)详解:双亲委派模型与破坏实践
java·开发语言·jvm·类加载·双亲委派