ChannelAwareMessageListener 与MessageListener

来源:互联网 发布:reshade画面优化补丁 编辑:程序博客网 时间:2024/05/21 14:54

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

public interface MessageListener {    void onMessage(Message message);}public interface ChannelAwareMessageListener {    void onMessage(Message message, Channel channel) throws Exception;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们会实现接口,并通过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>
  • 1
  • 2
  • 3
  • 4

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

org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.rollbackOnExceptionIfNecessary(Throwable)
  • 1
    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();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

isAutoAck方法

    public boolean isAutoAck() {        return this == NONE;    }
  • 1
  • 2
  • 3

可以看到,如果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;}
  • 1
  • 2
  • 3
  • 4

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

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

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

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

上面代码中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

原创粉丝点击