引言

在当今分布式系统的架构中,消息中间件扮演着举足轻重的角色,而 ActiveMQ 作为一款广泛使用的开源消息中间件,凭借其对 JMS(Java Message Service)规范的支持、多种消息传输协议、丰富的消息模型(如点对点和发布 / 订阅)以及出色的集群能力 ,成为众多开发者构建分布式系统的重要选择。在分布式系统中,消息的可靠传输至关重要,哪怕是偶尔出现的消息丢失或处理失败,都可能引发系统功能的异常,给业务带来严重影响。
为了确保消息能准确无误地被处理,ActiveMQ 引入了消息确认与重发机制。消息确认机制让生产者或消费者知晓消息的处理状态,重发机制则在消息处理出现异常时重新发送消息,这两种机制共同为消息传输的可靠性提供保障。接下来,本文将深入探讨 ActiveMQ 的消息确认与重发机制,包括它们的原理、工作方式、使用场景以及相关的代码示例。
ActiveMQ 消息确认机制
确认机制的概念与作用
消息确认机制是 ActiveMQ 确保消息可靠传输的关键机制,它定义了消息发送者(生产者)或接收者(消费者)与消息代理(broker)之间确认消息已被成功处理的方式 。在消息从生产者发送到 broker,再由消费者接收的过程中,确认机制起到了至关重要的作用。
对于生产者而言,确认机制让其知晓消息是否已被 broker 成功接收并存储,从而避免因网络故障等原因导致消息丢失而生产者却不知情的情况。对于消费者来说,确认机制确保了消息在被正确处理后才被标记为已消费,防止消息的重复处理或丢失。例如,在一个订单处理系统中,生产者发送订单消息给 broker,如果没有确认机制,生产者无法确定订单消息是否成功到达 broker,可能会导致订单的重复发送或丢失,而消费者如果没有确认已成功处理订单消息,可能会在系统故障恢复后再次处理该订单,导致重复下单。
JMS 规范中的确认模式
JMS 规范定义了四种确认模式,每种模式都有其独特的工作原理、适用场景和优缺点。
- AUTO_ACKNOWLEDGE(自动确认):当客户端成功从receive方法或onMessage方法返回时,会话会自动确认客户端已收到消息。例如,在一个简单的日志记录系统中,消费者接收到日志消息后,不需要额外的操作,消息就会被自动确认,这种模式简单易用,能提高消息处理的效率 。但它存在一定风险,如果在消息被确认后,业务处理过程中出现异常,消息已被从 broker 队列中删除,可能会导致数据不一致或业务逻辑错误。
- CLIENT_ACKNOWLEDGE(客户端手动确认):客户端通过调用message.acknowledge()方法来手动确认消息已被接收和处理。这种模式下,确认是在会话层进行的,确认一个被消费的消息将自动确认所有已消费的其他消息。例如,在一个订单支付系统中,消费者接收到支付消息后,只有在完成支付业务逻辑并调用acknowledge()方法后,消息才会被确认,这确保了消息处理的完整性和准确性 。不过,如果开发者忘记调用acknowledge()方法,可能会导致消息一直留在 broker 队列中,占用资源,并且如果在确认前客户端出现故障,消息可能会被重复发送和处理。
- DUPS_OK_ACKNOWLEDGE(自动批量确认):这种模式下,消息不是必须立即签收,可能会出现重复发送的情况。在第二次重新传送消息时,消息头的JmsDelivered会被置为true,标示当前消息已经传送过一次,客户端需要进行消息的重复处理控制。它适用于对消息重复不太敏感,追求高吞吐量的场景,如一些实时数据分析系统,少量的消息重复不会影响整体的分析结果,却能提高系统的处理速度 。但如果业务对消息的唯一性和准确性要求很高,这种模式就不太适用。
- SESSION_TRANSACTED(事务提交并确认):当会话使用事务时,使用此模式。在事务开启之后,和session.commit()之前,所有消费的消息,要么全部正常确认,要么全部重新投递(redelivery)。例如,在一个涉及多个数据库操作的复杂业务场景中,将消息消费和数据库操作放在一个事务中,只有当所有数据库操作都成功完成并提交事务时,消息才会被确认,否则整个事务回滚,消息重新投递,保证了数据的一致性和完整性 。但事务的使用会增加系统的复杂性和性能开销,因为事务的管理需要额外的资源和时间。
ActiveMQ 扩展的确认模式
ActiveMQ 补充了INDIVIDUAL_ACKNOWLEDGE确认模式,用于确认单条消息。与CLIENT_ACKNOWLEDGE模式相比,CLIENT_ACKNOWLEDGE模式调用message.acknowledge()方法会确认由此会话消费的所有消息,而INDIVIDUAL_ACKNOWLEDGE模式下,仅会确认调用acknowledge()方法的那条消息 。在一个任务处理系统中,每个任务都有独立的处理逻辑和状态,如果使用CLIENT_ACKNOWLEDGE模式,可能会因为一个任务的异常导致其他任务也无法正确确认,而INDIVIDUAL_ACKNOWLEDGE模式可以精确地确认每条任务消息,即使某个任务出现问题,也不会影响其他任务的确认和处理 。不过,由于需要对每条消息进行单独确认,在消息量较大时,可能会增加系统的开销和复杂性。
确认模式的实际应用与代码示例
以下是不同确认模式下,使用 ActiveMQ 发送和接收消息的代码示例:
- AUTO_ACKNOWLEDGE 模式
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;
public class AutoAcknowledgeExample {
public static void main(String[] args) throws JMSException {
// 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
// 创建连接
Connection connection = connectionFactory.createConnection();
// 启动连接
connection.start();
// 创建会话,参数false表示不使用事务,Session.AUTO_ACKNOWLEDGE表示自动确认模式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("testQueue");
// 创建生产者
MessageProducer producer = session.createProducer(destination);
// 创建消息
TextMessage message = session.createTextMessage("Hello, ActiveMQ!");
// 发送消息
producer.send(message);
System.out.println("Message sent: " + message.getText());
// 创建消费者
MessageConsumer consumer = session.createConsumer(destination);
// 接收消息
Message receivedMessage = consumer.receive();
if (receivedMessage instanceof TextMessage) {
TextMessage textMessage = (TextMessage) receivedMessage;
System.out.println("Received message: " + textMessage.getText());
}
// 关闭资源
consumer.close();
producer.close();
session.close();
connection.close();
}
}
在这段代码中,session.createSession(false, Session.AUTO_ACKNOWLEDGE)设置了会话的确认模式为AUTO_ACKNOWLEDGE,当消费者成功接收消息并从receive方法返回时,消息会自动被确认。
- CLIENT_ACKNOWLEDGE 模式
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;
public class ClientAcknowledgeExample {
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建会话,参数false表示不使用事务,Session.CLIENT_ACKNOWLEDGE表示客户端手动确认模式
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
Destination destination = session.createQueue("testQueue");
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage("Hello, ActiveMQ!");
producer.send(message);
System.out.println("Message sent: " + message.getText());
MessageConsumer consumer = session.createConsumer(destination);
Message receivedMessage = consumer.receive();
if (receivedMessage instanceof TextMessage) {
TextMessage textMessage = (TextMessage) receivedMessage;
System.out.println("Received message: " + textMessage.getText());
// 手动确认消息
receivedMessage.acknowledge();
System.out.println("Message acknowledged");
}
consumer.close();
producer.close();
session.close();
connection.close();
}
}
在这个示例中,session.createSession(false, Session.CLIENT_ACKNOWLEDGE)设置了手动确认模式,消费者在接收到消息并处理完成后,需要调用receivedMessage.acknowledge()手动确认消息。
- INDIVIDUAL_ACKNOWLEDGE 模式(ActiveMQ 扩展)
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQSession;
import javax.jms.*;
public class IndividualAcknowledgeExample {
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建会话,参数false表示不使用事务,ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE表示单条消息确认模式
Session session = connection.createSession(false, ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);
Destination destination = session.createQueue("testQueue");
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage("Hello, ActiveMQ!");
producer.send(message);
System.out.println("Message sent: " + message.getText());
MessageConsumer consumer = session.createConsumer(destination);
Message receivedMessage = consumer.receive();
if (receivedMessage instanceof TextMessage) {
TextMessage textMessage = (TextMessage) receivedMessage;
System.out.println("Received message: " + textMessage.getText());
// 确认单条消息
receivedMessage.acknowledge();
System.out.println("Individual message acknowledged");
}
consumer.close();
producer.close();
session.close();
connection.close();
}
}
这段代码展示了INDIVIDUAL_ACKNOWLEDGE模式的使用,session.createSession(false, ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE)设置了单条消息确认模式,消费者调用acknowledge()方法只会确认当前处理的这条消息。