Spring-amqp 1.6.1消费者手工对消息进行确认

Spring AMQP 消费者确认机制
本文探讨了在Spring AMQP框架中消费者如何处理消息接收及确认过程。介绍了默认自动确认机制的工作原理,以及如何通过手动确认避免不必要的消息重发。

前言

在使用Spring amqp创建消费者并接收消息时,通常会用到下面两个接口。

public interface MessageListener {

    void onMessage(Message message);

}

public interface ChannelAwareMessageListener {
    void onMessage(Message message, Channel channel) throws Exception;

}

我们会实现接口,并通过onMessage方法来接收消息。在接收消息后,处理业务时如果出现异常,那么消费者会不断接收到重发的消息。有时候在出现某些异常,无法处理,因此并不希望继续接收到重发,因此需要用到手工确认模式,来按需进行重发。

1.默认情况下为什么会自动重发?

在配置消费端时,通常使用下面的配置。而对于rabbit:listener-container标签并未指定“确认属性” acknowledge。默认情况下该属性为auto。

    <rabbit:listener-container 
        connection-factory="connectionFactory" >
        <rabbit:listener ref="consumer" method="listen" queue-names="myQueue" />
    </rabbit:listener-container>

当onMessage方法产生异常后,框架会调用下面的方法处理异常。

org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.rollbackOnExceptionIfNecessary(Throwable)
    public void rollbackOnExceptionIfNecessary(Throwable ex) throws Exception {

        boolean ackRequired = !this.acknowledgeMode.isAutoAck() && !this.acknowledgeMode.isManual();
        try {
            if (this.transactional) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Initiating transaction rollback on application exception: " + ex);
                }
                RabbitUtils.rollbackIfNecessary(this.channel);
            }
            if (ackRequired) {
                // We should always requeue if the container was stopping
                boolean shouldRequeue = this.defaultRequeuRejected ||
                        ex instanceof MessageRejectedWhileStoppingException;
                Throwable t = ex;
                while (shouldRequeue && t != null) {
                    if (t instanceof AmqpRejectAndDontRequeueException) {
                        shouldRequeue = false;
                    }
                    t = t.getCause();
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Rejecting messages (requeue=" + shouldRequeue + ")");
                }
                for (Long deliveryTag : this.deliveryTags) {
                    // With newer RabbitMQ brokers could use basicNack here...
                    this.channel.basicReject(deliveryTag, shouldRequeue);
                }
                if (this.transactional) {
                    // Need to commit the reject (=nack)
                    RabbitUtils.commitIfNecessary(this.channel);
                }
            }
        }
        catch (Exception e) {
            logger.error("Application exception overridden by rollback exception", ex);
            throw e;
        }
        finally {
            this.deliveryTags.clear();
        }
    }

isAutoAck方法

    public boolean isAutoAck() {
        return this == NONE;
    }

可以看到,如果acknowledge即非manul,也非none时(调用处方法名字是isAutoAck,但内部确实判断是否为NONE),那么会调用this.channel.basicReject。因此发送否定确认,最终不断收到重复发送的消息。

关于否认可以参考下面的链接。

http://www.rabbitmq.com/nack.html

2.对消息进行手工确认

为了在Spring-amqp框架中进行手工确认,在接收消息时需要实现如下的接口。

public interface ChannelAwareMessageListener {
    void onMessage(Message message, Channel channel) throws Exception;

}

此外消费者的确认模式需要配置为manual,其中确认模式包括NONE,MANUL,与AUTO三种[1]。

    <rabbit:listener-container 
        connection-factory="connectionFactory" acknowledge="manual">

那么当收到消息后,如果要否认,或确认则通过调用channel对象的下面的两个方法即可。其中basicAck进行确认,而basicNack进行否认。

        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        throw new IllegalArgumentException("Illegal");
        channel.basicAck(deliveryTag, false);
        channel.basicNack(deliveryTag, false, true);

上面代码中deliveryTag即消息消交付的一个标识,其作用域为channel。而basicNAck与basicReject都可以进行否则,二者区别参考下面的官网解释。

http://www.rabbitmq.com/nack.html

当在onMessage方法中调用basicAck确认消息后,队列中持久化的消息会被删除。而调用basicNack后,会收到rabbitmq重发的消息。若未调用basicAck确认则消息会产生堆积[2]。那么当消费者下再一次连接rabbitmq时消息会重发给消费者。

[1]Spring-amqp 配置消息的三种确认方式,http://docs.spring.io/spring-amqp/docs/1.6.5.RELEASE/reference/html/_reference.html 3.1.15 Message Listener Container Configuration
[2]消息确认 http://www.rabbitmq.com/tutorials/tutorial-two-java.html Forgotten acknowledgment

### 区别与关系 `spring-boot-starter-amqp`、`amqp-client` 和 `spring-amqp` 是在使用 AMQP(Advanced Message Queuing Protocol)协议进行消息传递时常见的三个组件,它们在功能和使用场景上有明确的分工和依赖关系。 `amqp-client` 是 RabbitMQ 官方提供的底层 Java 客户端库,用于与 RabbitMQ 服务器进行通信。它提供了基础的连接、通道、发布和消费消息的 API,适用于直接操作 RabbitMQ 的场景,但需要手动管理连接和资源[^1]。 `spring-amqp` 是 Spring 框架对 AMQP 协议的抽象和封装,提供了一套高层次的 API,简化了与 RabbitMQ 的集成。它基于 `amqp-client`,并提供了诸如 `RabbitTemplate`、消息监听容器等高级功能,使得开发者可以更方便地发送和接收消息[^1]。 `spring-boot-starter-amqp` 是 Spring Boot 提供的一个启动器依赖,集成了 `spring-amqp` 和 `amqp-client`,并提供了自动配置功能。通过简单的配置,即可快速构建基于 RabbitMQ消息生产者和消费者,极大地简化了开发流程[^1]。 ### 依赖关系示例 以下是一个典型的 `pom.xml` 配置片段,展示了 `spring-boot-starter-amqp` 如何集成其他两个库: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 此依赖会自动引入 `spring-amqp` 和 `amqp-client`,无需额外添加。 ### 功能对比 | 组件名称 | 功能描述 | 是否需要手动管理资源 | |------------------------|--------------------------------------------------------------------------|----------------------| | `amqp-client` | RabbitMQ 官方提供的基础客户端库,支持直接与 RabbitMQ 进行通信 | 是 | | `spring-amqp` | SpringAMQP 的封装,提供高层次的 API,简化消息操作 | 否 | | `spring-boot-starter-amqp` | Spring Boot 启动器,集成 `spring-amqp` 和 `amqp-client`,提供自动配置功能 | 否 | ### 使用场景 - **`amqp-client`**:适用于需要精细控制 RabbitMQ 连接和消息传递的场景,适合对 RabbitMQ 有深入了解的开发者。 - **`spring-amqp`**:适用于希望在 Spring 项目中快速集成 RabbitMQ,但不需要 Spring Boot 自动配置功能的场景。 - **`spring-boot-starter-amqp`**:适用于 Spring Boot 项目,希望快速集成 RabbitMQ 并利用自动配置功能简化开发流程的场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值