正常情况下:consumer 消费完消息后,会发送"标准确认"给 broker,这个确认对象以 MessageAck 类表征:

// 省略其他代码。类中定义了各种确认的类型
public class MessageAck extends BaseCommand {
public static final byte DATA_STRUCTURE_TYPE = CommandTypes.MESSAGE_ACK;
public static final byte DELIVERED_ACK_TYPE = 0;
public static final byte STANDARD_ACK_TYPE = 2;
public static final byte POSION_ACK_TYPE = 1;
public static final byte REDELIVERED_ACK_TYPE = 3;
public static final byte INDIVIDUAL_ACK_TYPE = 4;
public static final byte UNMATCHED_ACK_TYPE = 5;
public static final byte EXPIRED_ACK_TYPE = 6; protected byte ackType; // messageCount 表示确认的消息的数量,即 consumer 可以对消息进行批量确认
public MessageAck(Message message, byte ackType, int messageCount) {
this.ackType = ackType;
this.destination = message.getDestination();
this.lastMessageId = message.getMessageId();
this.messageCount = messageCount;
}
}

但是当 consumer 处理消息失败时,会怎样呢?例如:发生了除数为 0,抛出异常

consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
logger.info("Received: " + message);
float f = 1 / 0;
}
});

consumer 会进行重新投递,重新把消息给 listener 处理。具体流程是:consumer 消费消息失败,抛出异常,回滚,然后重新投递。

// void org.apache.activemq.ActiveMQMessageConsumer.rollback() throws JMSException
if (messageListener.get() != null) {
session.redispatch(this, unconsumedMessages);
}

下面的代码设置 RedeliveryPolicy:

RedeliveryPolicy queuePolicy = new RedeliveryPolicy();
queuePolicy.setInitialRedeliveryDelay(0);
queuePolicy.setRedeliveryDelay(1000);
queuePolicy.setUseExponentialBackOff(false);
queuePolicy.setMaximumRedeliveries(1);
RedeliveryPolicyMap map = connection.getRedeliveryPolicyMap();
map.put(new ActiveMQQueue(">"), queuePolicy);

这个重新投递策略是 consumer 端的(consumer 重新投递给自己的消息 listener),而不是 broker 重新投递给 consumer 的,理解这一点特别重要。超过了最大投递次数后,consumer 会发送给 broker 一个 POSION_ACK_TYPE 类型的 MessageAck 响应,正常情况是 STANDARD_ACK_TYPE 类型的。

consumer发送正常消息确认的调用栈:

主要逻辑在:

// void org.apache.activemq.ActiveMQMessageConsumer
private void afterMessageIsConsumed(MessageDispatch md, boolean messageExpired) throws JMSException {
if (unconsumedMessages.isClosed()) {
return;
}
if (messageExpired) {
// 过期消息
acknowledge(md, MessageAck.EXPIRED_ACK_TYPE);
stats.getExpiredMessageCount().increment();
} else {
stats.onMessage();
if (session.getTransacted()) {
// Do nothing.
} else if (isAutoAcknowledgeEach()) {
// 设置为 Session.AUTO_ACKNOWLEDGE
if (deliveryingAcknowledgements.compareAndSet(false, true)) {
synchronized (deliveredMessages) {
if (!deliveredMessages.isEmpty()) {
if (optimizeAcknowledge) {
ackCounter++; // AMQ-3956 evaluate both expired and normal msgs as
// otherwise consumer may get stalled
if (ackCounter + deliveredCounter >= (info.getPrefetchSize() * .65) || (optimizeAcknowledgeTimeOut > 0 && System.currentTimeMillis() >= (optimizeAckTimestamp + optimizeAcknowledgeTimeOut))) {
MessageAck ack = makeAckForAllDeliveredMessages(MessageAck.STANDARD_ACK_TYPE);
if (ack != null) {
deliveredMessages.clear();
ackCounter = 0;
session.sendAck(ack);
optimizeAckTimestamp = System.currentTimeMillis();
}
// AMQ-3956 - as further optimization send
// ack for expired msgs when there are any.
// This resets the deliveredCounter to 0 so that
// we won't sent standard acks with every msg just
// because the deliveredCounter just below
// 0.5 * prefetch as used in ackLater()
if (pendingAck != null && deliveredCounter > 0) {
session.sendAck(pendingAck);
pendingAck = null;
deliveredCounter = 0;
}
}
} else {
MessageAck ack = makeAckForAllDeliveredMessages(MessageAck.STANDARD_ACK_TYPE);
if (ack!=null) {
deliveredMessages.clear();
session.sendAck(ack);
}
}
}
}
deliveryingAcknowledgements.set(false);
}
} else if (isAutoAcknowledgeBatch()) {
ackLater(md, MessageAck.STANDARD_ACK_TYPE);
} else if (session.isClientAcknowledge()||session.isIndividualAcknowledge()) {
boolean messageUnackedByConsumer = false;
synchronized (deliveredMessages) {
messageUnackedByConsumer = deliveredMessages.contains(md);
}
if (messageUnackedByConsumer) {
ackLater(md, MessageAck.DELIVERED_ACK_TYPE);
}
}
else {
throw new IllegalStateException("Invalid session state.");
}
}
}

consumer 发送有毒消息确认的调用栈:

broker 接收消息确认的调用栈:

那么,在什么情况下,broker 会重新发送消息给 cosumer 呢?答案是:

broker 把一个消息推送给 consumer 后,但是还没收到任何确认,如果这时消费者断开连接,broker 会把这个消息加入到重新投递队列中,推送给其他的消费者。

ActiveMQ 消息的重新投递的更多相关文章

  1. 消息中间件-activemq消息机制和持久化介绍(三)

    前面一节简单学习了activemq的使用,我们知道activemq的使用方式非常简单有如下几个步骤: 创建连接工厂 创建连接 创建会话 创建目的地 创建生产者或消费者 生产或消费消息 关闭生产或消费者 ...

  2. IM消息送达保证机制实现(二):保证离线消息的可靠投递

    1.前言 本文的上篇<IM消息送达保证机制实现(一):保证在线实时消息的可靠投递>中,我们讨论了在线实时消息的投递可以通过应用层的确认.发送方的超时重传.接收方的去重等手段来保证业务层面消 ...

  3. 2015年12月10日 spring初级知识讲解(三)Spring消息之activeMQ消息队列

    基础 JMS消息 一.下载ActiveMQ并安装 地址:http://activemq.apache.org/ 最新版本:5.13.0 下载完后解压缩到本地硬盘中,解压目录中activemq-core ...

  4. Activemq消息类型

    Activemq消息类型JMS规范中的消息类型包括TextMessage.MapMessage.ObjectMessage.BytesMessage.和StreamMessage等五种.ActiveM ...

  5. ActiveMQ消息的可靠性机制(转)

    文章转自:http://www.linuxidc.com/Linux/2013-02/79664.htm 1.JMS消息确认机制 JMS消息只有在被确认之后,才认为已经被成功地消费了.消息的成功消费通 ...

  6. activemq消息队列的使用及应用docker部署常见问题及注意事项

    activemq消息队列的使用及应用docker部署常见问题及注意事项 docker用https://hub.docker.com/r/rmohr/activemq/配置在/data/docker/a ...

  7. JAVA的设计模式之观察者模式----结合ActiveMQ消息队列说明

    1----------------------观察者模式------------------------------ 观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的 ...

  8. 深入浅出 JMS(三) - ActiveMQ 消息传输

    深入浅出 JMS(三) - ActiveMQ 消息传输 一.消息协商器(Message Broker) broke:消息的交换器,就是对消息进行管理的容器.ActiveMQ 可以创建多个 Broker ...

  9. activemq消息重发机制[转]

    大家知道,JMS规范中,Message消息头接口中有setJMSRedelivered(boolean redelivered)和getJMSRedelivered()方法,用于设置和获取消息的重发标 ...

随机推荐

  1. super()、this属性与static静态方法的执行逻辑

    1.super的构造顺序:永远优先构造父类的方法 2.static永远在类实例之前执行,this的使用范围为实例之后

  2. Educational Codeforces Round 23 C. Really Big Numbers 暴力

    C. Really Big Numbers time limit per test 1 second memory limit per test 256 megabytes input standar ...

  3. node项目配置成nginx启动

    node项目配置成nginx启动 1.新建ant.conf upstream antNodeJs{ server 127.0.0.1:8000; keepalive 64; } server { li ...

  4. Tomcat部署项目定时任务跑了两次

    在server.xml下面找到Host这行代码 <Host name="localhost" appBase="webapps" unpackWARs=& ...

  5. 批标准化 Batch Normalization

    2018-12-05 20:28:15 在机器学习领域有一个很重要的假设,即独立同分布假设,也就是说训练集和测试集是满足相同分布的,这是通过训练数据获得的模型能够在测试集获得好的效果的一个基本保障.而 ...

  6. Nginx自学笔记

    Nginx相关 标签(空格分隔): nginx 享学 安装部署 通过源代码的方式安装 使用 ./sbin/nginx #启动 ./sbin/nginx -t #检查是否有错 ./sbin/nginx ...

  7. Top 命令解析

    TOP是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止.比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中C ...

  8. 雷林鹏分享:服务器上的 XML

    服务器上的 XML XML 文件是类似 HTML 文件的纯文本文件. XML 能够通过标准的 Web 服务器轻松地存储和生成. 在服务器上存储 XML 文件 XML 文件在 Internet 服务器上 ...

  9. 雷林鹏分享:C# 可空类型(Nullable)

    C# 可空类型(Nullable) C# 可空类型(Nullable) C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 nu ...

  10. spring ----> 事务:传播机制和接口TransactionDefinition

    spring事务: 编程式事务(细粒度) 声明式事务(粗粒度,xml或者注解格式) spring接口TransactionDefinition: TransactionDefinition接口定义了事 ...