本篇文章详细介绍了 RocketMQ 的事务消息、顺序消息与延时消息这三大高级特性,包括原理、CentOS 7 下的配置与使用方法,以及对应的应用场景。
事务消息是 RocketMQ 为解决分布式事务一致性问题而设计的特性,基于两阶段提交(2PC)实现。其核心流程如下:
1、第一阶段:Half Message:Producer 发送一条 “半消息” 到 Broker。半消息与普通消息的区别在于,此时 Consumer 无法消费该消息。Broker 接收到半消息后,会将其持久化并返回响应给 Producer。
2、第二阶段:本地事务执行:Producer 收到 Broker 对半消息的确认响应后,开始执行本地事务。
3、第二阶段:消息状态回查:若 Producer 在执行本地事务过程中出现异常,或因网络问题无法及时向 Broker 发送事务状态,Broker 会定时对这些半消息进行回查,询问 Producer 该消息对应的本地事务是否执行成功,以确定最终的消息状态。
4、第二阶段:消息提交或回滚:根据本地事务的执行结果,Producer 向 Broker 发送 Commit 或 Rollback 指令。若本地事务执行成功,发送 Commit 指令,Broker 将半消息标记为可消费状态;若执行失败,发送 Rollback 指令,Broker 删除该半消息。
1、配置文件修改:在 CentOS 7 上,若使用 RocketMQ 自带示例代码,通常无需额外修改配置文件。但在实际项目中,如需调整事务消息相关参数,可修改 Broker 的配置文件broker.conf。例如,调整事务消息回查间隔时间,可添加如下配置:
transactionCheckInterval=60000 # 单位为毫秒,这里设置为1分钟
2、代码示例:
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.concurrent.atomic.AtomicInteger;
public class TransactionProducer {
private static final AtomicInteger TRANSACTION_ID = new AtomicInteger(0);
public static void main(String[] args) throws MQClientException, InterruptedException {
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_group");
producer.setNamesrvAddr("localhost:9876");
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message message, Object o) {
// 模拟本地事务执行
try {
Thread.sleep(2000);
// 这里可根据业务逻辑返回不同状态
return LocalTransactionState.COMMIT_MESSAGE;
} catch (InterruptedException e) {
e.printStackTrace();
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
System.out.println("Checking transaction for message: " + messageExt.getMsgId());
// 可根据消息ID等信息查询本地事务状态
return LocalTransactionState.COMMIT_MESSAGE;
}
});
producer.start();
Message msg = new Message("TransactionTopic", "TagA",
("Transaction Message " + TRANSACTION_ID.incrementAndGet()).getBytes());
producer.sendMessageInTransaction(msg, null);
Thread.sleep(10000);
producer.shutdown();
}
}
在上述代码中,创建了TransactionMQProducer实例,设置 NameServer 地址和TransactionListener。TransactionListener接口的executeLocalTransaction方法用于执行本地事务,checkLocalTransaction方法用于处理 Broker 的事务状态回查。通过sendMessageInTransaction方法发送事务消息。
事务消息适用于电商订单处理、金融转账等对数据一致性要求较高的场景。例如在电商系统中,用户下单后,需要同时扣减库存和生成订单,这两个操作可通过事务消息确保要么都成功,要么都失败。
顺序消息是指消息按照特定的顺序进行发送和消费。RocketMQ 通过将同一业务的消息发送到同一个队列,并确保消费者按顺序消费该队列中的消息,实现消息的顺序性。可分为分区有序和全局有序:
1、配置文件修改:一般情况下,顺序消息无需特殊修改配置文件。但在实际应用中,可根据业务需求调整 Broker 的队列数量等参数,在broker.conf中修改:
defaultTopicQueueNums=4 # 设置默认Topic的队列数量
2、代码示例:
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import java.util.List;
public class OrderlyProducer {
public static void main(String[] args) throws MQClientException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("orderly_producer_group");
producer.setNamesrvAddr("localhost:9876");
producer.start();
String[] orderIds = {"1001", "1002", "1001", "1002"};
for (int i = 0; i < orderIds.length; i++) {
Message msg = new Message("OrderlyTopic", "TagA",
("Order Message " + i).getBytes());
producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List mqs, Message msg, Object arg) {
String orderId = (String) arg;
int index = Math.abs(orderId.hashCode() % mqs.size());
return mqs.get(index);
}
}, orderIds[i]);
}
producer.shutdown();
}
}
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
public class OrderlyConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("orderly_consumer_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("OrderlyTopic", "*");
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext context) {
for (org.apache.rocketmq.common.message.MessageExt msg : msgs) {
System.out.println("Consumed message: " + new String(msg.getBody()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("Consumer started");
}
}
在生产者代码中,通过MessageQueueSelector将具有相同orderId的消息发送到同一个队列;消费者代码中,通过MessageListenerOrderly按顺序消费消息。
顺序消息适用于日志处理、订单处理流程、数据库 binlog 同步等场景。例如在数据库 binlog 同步中,需要保证 SQL 语句的执行顺序,以确保数据的一致性。
延时消息是指消息在发送后,不会立即被消费者消费,而是在经过指定的延迟时间后,才进入可消费状态。RocketMQ 通过将延时消息存储在特定的队列中,并根据延迟时间进行调度,实现延时功能。
1、配置文件修改:RocketMQ 默认支持 18 个延时等级,对应不同的延迟时间,可在broker.conf中查看或修改:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
2、代码示例:
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
public class ScheduledProducer {
public static void main(String[] args) throws MQClientException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("scheduled_producer_group");
producer.setNamesrvAddr("localhost:9876");
producer.start();
Message msg = new Message("ScheduledTopic", "TagA",
("Scheduled Message").getBytes());
// 设置延时等级为3,对应10秒延迟
msg.setDelayTimeLevel(3);
producer.send(msg);
producer.shutdown();
}
}
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
public class ScheduledConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("scheduled_consumer_group");
consumer.setNamesrvAddr("localhost:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("ScheduledTopic", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.println("Consumed scheduled message: " + new String(msg.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("Consumer started");
}
}
在生产者代码中,通过msg.setDelayTimeLevel设置消息的延时等级;消费者代码与普通消息消费类似,接收并处理延时消息。
延时消息适用于订单超时取消、任务定时调度、优惠券过期提醒等场景。例如在电商系统中,用户下单后若长时间未支付,可通过延时消息实现订单自动取消。