ActiveMQ消息确认机制

本文深入探讨ActiveMQ的消息确认机制,包括消息生命周期、ACK模式、ACK类型及其源码分析,阐述了在不同场景下消息的处理与确认策略,并讨论了如何通过优化ACK提升性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

在巡更组件中,通过下图所示流程获取门禁,报警器事件来完成巡更任务,流程中有两处使用了ActiveMQ来传输消息,那么消息在传输的过程中ActiveMQ是如何保障消息传输的可靠性,本文将通过介绍ActiveMQ的消息确认机制来进行分析。
[外链图片转存失败(img-yoMs2Vp3-1565192181485)(http://10.1.65.34/group1/M00/00/98/CgFBIlx32SOAHP4NAABLfIVNcBU489.png)]

消息生命周期

我们先看一下activemq中,一条消息的生命周期如下图所示:
在这里插入图片描述
图片中简单的描述了一条消息的生命周期,不过在不同的架构环境中,message的流动性可能更加复杂。一条消息从producer端发出之后,一旦被broker正确保存,那么它将会被consumer消费,然后ACK,broker端才会删除;不过当消息过期或者存储设备溢出时,也会终结它。

ACK机制

ACK模式描述了Consumer与broker确认消息的方式(时机),比如当消息被Consumer接收之后,Consumer将在何时确认消息。对于broker而言,只有接收到ACK指令,才会认为消息被正确的接收或者处理成功了,通过ACK,可以在consumer(/producer)与Broker之间建立一种简单的“担保”机制。

在JMS的API中定义了如下4中确认机制

static final int AUTO_ACKNOWLEDGE = 1;
static final int CLIENT_ACKNOWLEDGE = 2;
static final int DUPS_OK_ACKNOWLEDGE = 3;
static final int SESSION_TRANSACTED = 0;
static final int INDIVIDUAL_ACKNOWLEDGE = 4;

AUTO_ACKNOWLEDGE = 1:自动确认
当client端成功的从receive方法或从onMessage(Message message) 方法返回的时候,会话自动确认client收到消息
CLIENT_ACKNOWLEDGE = 2: 客户端手动确认
客户端通过调用acknowledge方法来确认客户端收到消息。但需要注意在这种应答模式下,确认是在会话层上进行的,确认一个被消费的消息将自动确认所有已消费的其他消息。比如一个消费者已经消费了10条消息,然后确认了第5条消息被消费,则这10条都被确认消费了
DUPS_OK_ACKNOWLEDGE = 3:自动批量确认
不是必须签收,消息可能会重复发送。在第二次重新传送消息的时候,消息头的JmsDelivered会被置为true标示当前消息已经传送过一次,客户端需要进行消息的重复处理控制
SESSION_TRANSACTED = 0:事务提交并确认
当session使用事务时,就是使用此模式。在事务开启之后,和session.commit()之前,所有消费的消息,要么全部正常确认,要么全部redelivery

此外ActiveMQ补充了一个自定义的ACK模式:
INDIVIDUAL_ACKNOWLEDGE = 4 : 单条确认模式
单条消息确认,这种确认模式,我们很少使用,它的确认时机和CLIENT_ACKNOWLEDGE几乎一样,当消息消费成功之后,需要调用message.acknowledege来确认此消息(单条),而CLIENT_ACKNOWLEDGE模式先message.acknowledge()方法将导致整个session中所有消息被确认(批量确认)

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;

Client端指定了ACK_MODE,但是在Client与broker在交换ACK指令的时候,还需要告知ACK_TYPE,ACK_TYPE表示此确认指令的类型,不同的ACK_TYPE将传递着消息的状态,broker可以根据不同的ACK_TYPE对消息进行不同的操作。
ActiveMQ中定义了如下几种ACK_TYPE:
DELIVERED_ACK_TYPE = 0 消息"已接收",但尚未处理结束
STANDARD_ACK_TYPE = 2 “标准"类型,通常表示为消息"处理成功”,broker端可以删除消息了
POSION_ACK_TYPE = 1 消息"错误",通常表示"抛弃"此消息,比如消息重发多次后,都无法正确处理时,消息将会被删除或者DLQ(死信队列)
REDELIVERED_ACK_TYPE = 3 消息需"重发",比如consumer处理消息时抛出了异常,broker稍后会重新发送此消息
INDIVIDUAL_ACK_TYPE = 4 表示只确认"单条消息",无论在任何ACK_MODE下
UNMATCHED_ACK_TYPE = 5 在Topic中,如果一条消息在转发给“订阅者”时,发现此消息不符合Selector过滤条件,那么此消息将 不会转发给订阅者,消息将会被存储引擎删除(相当于在Broker上确认了消息)
EXPIRED_ACK_TYPE=6 消息如果过期了,则发送过期指令

源码分析

下面从同步阻塞获取消息的方法的源码中分析其中的原理。
我们通过consumer.receive()来同步接收消息。

public Message receive() throws JMSException {
   
	  //检查连接是否已关闭
	  checkClosed();
	  //检查是否配置了消息监听器,listener与当前同步接收消息冲突,抛出异常
	  checkMessageListener();
	  //发送拉取消息指令
	  sendPullCommand(0);
	  //从本地队列获取消息
	  MessageDispatch md = dequeue(-1);
	  if (md == null) {
   
	      return null;
	  }
	  //消息消费前准备
	  beforeMessageIsConsumed(md);
	  //消息消费后准备
	  afterMessageIsConsumed(md, false);
	
	  return createActiveMQMessage(md);
}

以上是客户端消费消息的总体流程,看一下拉取消息指令sendPullCommand的源码如下:

sendPullCommand

/**
 * If we have a zero prefetch specified then send a pull command to the
 * broker to pull a message we are about to receive
 */
protected void sendPullCommand(long timeout) throws JMSException {
   
	//清空在deliveredMessages中还未ack的消息
    clearDeliveredList();
    if (info.getCurrentPrefetchSize() == 0 && unconsumedMessages.isEmpty()) {
   
        MessagePull messagePull = new MessagePull();
        messagePull.configure(info);
        messagePull.setTimeout(timeout);
        session.asyncSendPacket(messagePull);
    }
}

clearDeliveredList 是为了清空再deliveredMessages中还未ack的消息,具体的操作如下,如果是事务性的会话,则将deliveredMessages中的消息放入previouslyDeliveredMessages列表做本地重发;如果是客户端应答方式,回滚重复的消息。
sendPullCommand方法还检查PrefetchSize这个变量是否为0和的unconsumedMessages(未曾消费的消息)是否为空,满足这两个条件才会发送拉取消息指令到broker端,broker端会推送消息到unconsumedMessages

private
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值