protected void onWaitEnd() {
long timeout = brokerController.getBrokerConfig().getTransactionTimeOut(); // @1
int checkMax = brokerController.getBrokerConfig().getTransactionCheckMax(); // @2
long begin = System.currentTimeMillis();
log.info(“Begin to check prepare message, begin time:{}”, begin);
this.brokerController.getTransactionalMessageService().check(timeout, checkMax, this.brokerController.getTransactionalMessageCheckListener()); // @3
log.info(“End to check prepare message, consumed time:{}”, System.currentTimeMillis() - begin);
}
代码@1:从broker配置文件中获取transactionTimeOut参数值,表示事务的过期时间,一个消息的存储时间 + 该值 大于系统当前时间,才对该消息执行事务状态会查。
代码@2:从broker配置文件中获取transactionCheckMax参数值,表示事务的最大检测次数,如果超过检测次数,消息会默认为丢弃,即rollback消息。
接下来重点分析TransactionalMessageService#check的实现逻辑,其实现类:org.apache.rocketmq.broker.transaction.queue.TransactionalMessageServiceImpl
TransactionalMessageServiceImpl#check
String topic = MixAll.RMQ_SYS_TRANS_HALF_TOPIC;
Set msgQueues = transactionalMessageBridge.fetchMessageQueues(topic);
if (msgQueues == null || msgQueues.size() == 0) {
log.warn(“The queue of topic is empty :” + topic);
return;
}
step1:根据主题名称,获取该主题下所有的消息队列。
TransactionalMessageServiceImpl#check
for (MessageQueue messageQueue : msgQueues) {
// …
}
Step2:循环遍历消息队列,从单个消息消费队列去获取消息。
TransactionalMessageServiceImpl#check
long startTime = System.currentTimeMillis();
MessageQueue opQueue = getOpQueue(messageQueue);
long halfOffset = transactionalMessageBridge.fetchConsumeOffset(messageQueue);
long opOffset = transactionalMessageBridge.fetchConsumeOffset(opQueue);
log.info(“Before check, the queue={} msgOffset={} opOffset={}”, messageQueue, halfOffset, opOffset);
if (halfOffset < 0 || opOffset < 0) {
log.error(“MessageQueue: {} illegal offset read: {}, op offset: {},skip this queue”, messageQueue, halfOffset, opOffset);
continue;
}
Step3:获取对应的操作队列,其主题为:RMQ_SYS_TRANS_OP_HALF_TOPIC,然后获取操作队列的消费进度、待操作的消费队列的消费进度,如果任意一小于0,忽略该消息队列,继续处理下一个队列。
TransactionalMessageServiceImpl#check
List doneOpOffset = new ArrayList<>();
HashMap<Long, Long> removeMap = new HashMap<>();
PullResult pullResult = fillOpRemoveMap(removeMap, opQueue, opOffset, halfOffset, doneOpOffset);
if (null == pullResult) {
log.error(“The queue={} check msgOffset={} with opOffset={} failed, pullResult is null”,
messageQueue, halfOffset, opOffset);
continue;
}
Step4:调用fillOpRemoveMap主题填充removeMap、doneOpOffset数据结构,这里主要的目的是避免重复调用事务回查接口,这里说一下RMQ_SYS_TRANS_HALF_TOPIC、RMQ_SYS_TRANS