三事务型MQ的最终一致性事务方案--1RocketMQ事务消息的发送与提交

三 事务型MQ的最终一致性事务方案--1RocketMQ事务消息的发送与提交

该方案,在MQ事务最终一致性方案的基础上,使用RocketMQ提供的事务消息功能。其简化了非事务型MQ最终一致性方案的流程。如下:

image-20230612180003947

流程如下:

事务消息的发送与commit/rollback阶段:
	1 producer发送half消息给broker(此时不能消费);
	2 服务端响应;
	3 执行本地事务---executeLocalTransaction;
	4 根据本地事务执行情况,commit或者rollback;
	
事务消息的补偿阶段:
	1 如果broker长时间没有收到本地事务执行状态,会向producer发起回查事务状态的操作;
	2 producer收到请求,检查本地事务执行状态---checkLocalTransaction;
	3 根据本地事务的回查结果,执行commit或者rollback------补偿,用在producer发送commit/rollback操作时,发生超时或者失败的情况

具体开发流程如下:

producer端:
step1:在controller中,定义响应request的方法{
 1 执行相应的业务流程;
 2 生成事务消息的唯一业务id(snowFlake);
 3 组装业务信息(包括事务唯一id)到消息体---方便消费端进行幂等消费;
 4 调用producer发送事务prepare的half消息;
}
/** 注意: 
通常开发过程中,在controller中调用service服务时,开启事务,事务执行成功后,向MQ发送half事务消息;
executeLocalTransaction方法,只执行事务信息的本地持久化操作;
在回查方法checkLocalTransaction中,只查询事务消息*/

ste2:实现TransactionListener创建事务信息监听器,并实现executeLocalTransaction(执行本地事务方法)和checkLocalTransaction(检查事务执行状态){
  /**该方法完成两个任务:
1 向本地事务消息数据库添加记录;2 设置本地事务的状态,建议返回LocalTransactionState.UNKNOW.
该方法与业务方代码(调用业务的@Transactional方法)同在一个事务中,只要本地事务提交成功,本方法也会提交成功*/
  executeLocalTransaction(message,object){
  	try{
      xxService.doTrans;//调用@Transactional方法,之心本地事务
      state=LocalTransactionState.UNKNOW;
       }catch(exception){
      state=rollback_message;
    }
    returen state;
  }
  
  /** 当前方法通过检查本地事务数据库中,是否有记录,来告知rocketMQ是commit还是rollback
  如果存在记录,commit;
  如果不存在,需设置查询次数:超过指定次数,未查询到消息,rollback;如果指定次数内未查到,unknown;
  rocketmq设置一定频率回查事务,此外查询次数设置为15次*/
  checkLocalTransaction{
  //略…………………………
  }
}


consumer端:
  

3.1 RocketMQ对事务消息的支持

其实根据上个小结的开发逻辑,可以知道,RocketMQ支持事务型消息,它的事务功能的实现,借助两阶段提交和定时事务状态回查决定commit/rollback。RocketMQ的事务消息实现原理如下:

image-20230612175945035

1 应用程序在事务内完成相关业务操作后,调用RocketMQ的事务消息发送接口,发送prepare的half事务消息;

2 rocketMQ的server在收到类型prepare的消息,会首先备份消息的topic和consumerQueue,然后将消息存储在topic为RMQ_SYS_TRANS_HALF_TOPIC的消息消费队列中;

half发送成功后,MQServer回调producer的transactionListener监听器,executeLocalTransaction记录本地事务信息持久化(注意:当前方法,和本地业务操作同属一个事务,来确保消息发送与本地事务的原子性);

4 rocketMQ的server长时间没有收到producer端事务执行状态(或者只收到unknown)时,会开启定时任务,消费RMQ_SYS_TRANS_HALF_TOPIC主题消息,向producer端发起事务状态回查的请求;

5 producer端收到请求后,执行checkLocalTransaction查询本地事务消息表,决定事务状态:

​ case1:如果查到数据,commit提交;

​ case2:如果查不到:记录查询次数,如果未超过次数,返回unknown;如果超过次数,返回rollback。

rocketMQ允许设置回查间隔与回查次数,默认15次。

3.2 RocketMQ事务消息的源码分析

3.2.1 事务消息发送流程

1 RocketMQ事务消息相关类

image-20230531123924947

1.1 TransactionMQProducer事务消息生产者对象
public class TransactionMQProducer extends DefaultMQProducer {

    private int checkThreadPoolMinSize = 1;
    private int checkThreadPoolMaxSize = 1;
    private int checkRequestHoldMax = 2000;
//事务状态回查的异步执行线程池
    private ExecutorService executorService;
//事务监听器,主要完成本地事务信息持久化、事务状态回查
    private TransactionListener transactionListener;
  
    @Override
    public void start() throws MQClientException {
        this.defaultMQProducerImpl.initTransactionEnv();
        super.start();
    }
  
  //事务消息发送
      @Override
    public TransactionSendResult sendMessageInTransaction(final Message msg,
        final Object arg) throws MQClientException {
        if (null == this.transactionListener) {
            throw new MQClientException("TransactionListener is null", null);
        }

        msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));
      //调用defaultMQProducerImpl的事务消息发送方法
        return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);
    }
1.2 TransactionListener事务监听器
public interface TransactionListener {
    /**
     * When send transactional prepare(half) message succeed, this method will be invoked to execute local transaction.
     *
     * @param msg Half(prepare) message
     * @param arg Custom business parameter
     * @return Transaction state
     */
    LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);

    /**
     * When no response to prepare(half) message. broker will send check message to check the transaction status, and this
     * method will be invoked to get local transaction status.
     *
     * @param msg Check message
     * @return Transaction state
     */
    LocalTransactionState checkLocalTransaction(final MessageExt msg);
}

2 源码流程

首先,也业务模块中调用发送half事务消息,

………………producer端发送……………
TAG1 TransactionMQProducer.sendMessageInTransaction
public class TransactionMQProducer extends DefaultMQProducer {

    @Override
    public TransactionSendResult sendMessageInTransaction(final Message msg,
        final Object arg) throws MQClientException {
        if (null == this.transactionListener) {
            throw new MQClientException("TransactionListener is null", null);
        }
//设置msg的top信息
        msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));
      //调用DefaultMQProducerImpl的方法
        return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);
    }

defaultMQProducerImpl是DefaultMQProducer内的属性,响应的方法,统统交给impl实现

DefaultMQProducerImpl
  
    public TransactionSendResult sendMessageInTransaction(final Message msg,
        final LocalTransactionExecuter localTransactionExecuter, final Object arg)
        throws MQClientException {
  //this.defaultMQProducer,如果是TransactionMQProducer,则从中中获取监听器
        TransactionListener transactionListener = getCheckListener();
        if (null == localTransactionExecuter && null == transactionListener) {
            throw new MQClientException("tranExecutor is null", null);
        }

        // 忽略配置的 DelayTimeLevel 延迟时间级别的参数
        if (msg.getDelayTimeLevel() != 0) {
            MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_DELAY_TIME_LEVEL);
        }

        Validators.checkMessage(msg, this.defaultMQProducer);

        SendResult sendResult = null;
  /**…………………………………………………………………………………………为消息添加属性……………………………………………………………………………………………………*/
  //STEP1 为消息添加prepared、producer-group属性。设置producerGroup目的是,在查询local事务状态时,可以从group中随机选择一个producer即可
  		//设置参数prepared为true
        MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
  		//设置消息所在的生产者组
        MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());
        try {
          //TAG2 this.send(msg)
   //STEP2 同步调用,发送消息到rocketMQ
            sendResult = this.send(msg);
        } catch (Exception e) {
            throw new MQClientException("send message Exception", e);
        }
/** …………………………………………………………………………根据sendResult的发送状态,确定本地事务的状态………………………………………………………………*/
        LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;
        Throwable localException = null;
        switch (sendResult.getSendStatus()) {
            case SEND_OK: {
                try {
                  //为消息添加事务id的属性
                    if (sendResult.getTransactionId() != null) {
                        msg.putUserProperty("__transactionId__", sendResult.getTransactionId());
                    }
                    String transactionId = msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
                    if (null != transactionId && !"".equals(transactionId)) {
                        msg.setTransactionId(transactionId);
                    }
                  //传入为null,不执行
                    if (null != localTransactionExecuter) {
                        localTransactionState = localTransactionExecuter.executeLocalTransactionBranch(msg, arg);
                    } else if (transactionListener != null) {
                        log.debug("Used new transaction API");
                      //step3 执行本地事务executeLocalTransaction方法
                        localTransactionState = transactionListener.executeLocalTransaction(msg, arg);
                    }
                    if (null == localTransactionState) {
                        localTransactionState = LocalTransactionState.UNKNOW;
                    }

                    if (localTransactionState != LocalTransactionState.COMMIT_MESSAGE) {
                        log.info("executeLocalTransactionBranch return {}", localTransactionState);
                        log.info(msg.toString());
                    }
                } catch (Throwable e) {
                    log.info("executeLocalTransactionBranch exception", e);
                    log.info(msg.toString());
                    localException = e;
                }
            }
            break;
            case FLUSH_DISK_TIMEOUT:
            case FLUSH_SLAVE_TIMEOUT:
            case SLAVE_NOT_AVAILABLE:
                localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;
                break;
            default:
                break;
        }

  //step4 结束事务
        try {
            this.endTransaction(sendResult, localTransactionState, localException);
        } catch (Exception e) {
            log.warn("local transaction execute " + localTransactionState + ", but end broker transaction failed", e);
        }

  //step5 构造事务消息发送的方法返回result
        TransactionSendResult transactionSendResult = new TransactionSendResult();
        transactionSendResult.setSendStatus(sendResult.getSendStatus());
        transactionSendResult.setMessageQueue(sendResult.getMessageQueue());
        transactionSendResult.setMsgId(sendResult.getMsgId());
        transactionSendResult.setQueueOffset(sendResult.getQueueOffset());
        transactionSendResult.setTransactionId(sendResult.getTransactionId());
        transactionSendResult.setLocalTransactionState(localTransactionState);
        return transactionSendResult;
    }

事务消息的发送过程,主要有如下步骤:

STEP1 为消息添加prepared、producer-group属性。设置producerGroup目的是,在查询local事务状态时,可以从group中随机选择一个producer即可;
STEP2 调用同步发送,发送消息到rocketMQ;

STEP3 根据sendResult的发送状态,确定本地事务的状态
	CASE1 OK当发送结果成功时,执行本地事务executeLocalTransaction方法,来设置事务执行状态;
	CASE2 如果消息刷新超时,slave不可用,标记事务状态为rollback;
	CASE3 其他情况,事务状态仍旧是unknown
STEP4 结束事务 this.endTransaction();
STEP5 构造事务消息发送的方法返回result;

在事务消息发送过程中,保证事务一致性的操作,比较关键的步骤是,对事务状态LocalTransactionState的设置:

1 在prepared消息发送成功时,执行本地事务方法,确定自己状态;(也会有commit、rollback、unknown)

如果prepared发送,出现刷新超时和slave不可用,会rollback状态;

3 其他情况的发送失败,会标记事务状态为unknown;

TAG2 this.send(msg) 事务消息发送

具体的发送流程,和普通消息相同,只是对于prepared消息,有如下差别:

DefaultMQProducerImpl
private SendResult sendKernelImpl(final Message msg,){

      final String tranMsg = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
                if (tranMsg != null && Boolean.parseBoolean(tranMsg)) {
                    sysFlag |= MessageSysFlag.TRANSACTION_PREPARED_TYPE;
                }
}

在消息发送前,为消息的flag添加prepared的标志位。

………………broker端处理………………

broker端接收到producer的请求,对应的处理逻辑由BrokerController中注册的processor方法实现。

image-20230531180415574

此处,对于sendMessage的请求,由sendProcessor处理。

TAG3 SendMessageProcessor.request--->sendMessage
public class SendMessageProcessor extends AbstractSendMessageProcessor implements NettyRequestProcessor {

    private List<ConsumeMessageHook> consumeMessageHookList;
    
       private RemotingCommand sendMessage(final ChannelHandlerContext ctx,
                                        final RemotingCommand request,
                                        final SendMessageContext sendMessageContext,
                                        final SendMessageRequestHeader requestHeader) throws RemotingCommandException {

        final RemotingCommand response = RemotingCommand.createResponseCommand(SendMessageResponseHeader.class);
        final SendMessageResponseHeader responseHeader = (SendMessageResponseHeader)response.readCustomHeader();

        response.setOpaque(request.getOpaque());

        response.addExtField(MessageConst.PROPERTY_MSG_REGION, this.brokerController.getBrokerConfig().getRegionId());
        response.addExtField(MessageConst.PROPERTY_TRACE_SWITCH, String.valueOf(this.brokerController.getBrokerConfig().isTraceOn()));

        log.debug("receive SendMessage request command, {}", request);

        final long startTimstamp = this.brokerController.getBrokerConfig().getStartAcceptSendRequestTimeStamp();
        if (this.brokerController.getMessageStore().now() < startTimstamp) {
            response.setCode(ResponseCode.SYSTEM_ERROR);
            response.setRemark(String.format("broker unable to service, until %s", UtilAll.timeMillisToHumanString2(startTimstamp)));
            return response;
        }

        response.setCode(-1);
        super.msgCheck(ctx, requestHeader, response);
        if (response.getCode() != -1) {
            return response;
        }

        final byte[] body = request.getBody();

        int queueIdInt = requestHeader.getQueueId();
        TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());

        if (queueIdInt < 0) {
            queueIdInt = Math.abs(this.random.nextInt() % 99999999) % topicConfig.getWriteQueueNums();
        }

        MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
        msgInner.setTopic(requestHeader.getTopic());
        msgInner.setQueueId(queueIdInt);

        if (!handleRetryAndDLQ(requestHeader, response, request, msgInner, topicConfig)) {
            return response;
        }

        msgInner.setBody(body);
        msgInner.setFlag(requestHeader.getFlag());
        MessageAccessor.setProperties(msgInner, MessageDecoder.string2messageProperties(requestHeader.getProperties()));
        msgInner.setPropertiesString(requestHeader.getProperties());
        msgInner.setBornTimestamp(requestHeader.getBornTimestamp());
        msgInner.setBornHost(ctx.channel().remoteAddress());
        msgInner.setStoreHost(this.getStoreHost());
        msgInner.setReconsumeTimes(requestHeader.getReconsumeTimes() == null ? 0 : requestHeader.getReconsumeTimes());
        PutMessageResult putMessageResult = null;
         //从message中获取属性集合
        Map<String, String> oriProps = MessageDecoder.string2messageProperties(requestHeader.getProperties());
         //获取消息是否是事务性prepared消息
        String traFlag = oriProps.get(MessageConst.PROPERTY_TRANSACTION_PREPARED);
        if (traFlag != null && Boolean.parseBoolean(traFlag)) {
            if (this.brokerController.getBrokerConfig().isRejectTransactionMessage()) {
                response.setCode(ResponseCode.NO_PERMISSION);
                response.setRemark(
                    "the broker[" + this.brokerController.getBrokerConfig().getBrokerIP1()
                        + "] sending transaction message is forbidden");
                return response;
            }
          //TAG3.1 brokerController.getTransactionalMessageService().prepareMessage(msgInner)
          //如果是事务性prepared消息,调用
            putMessageResult = this.brokerController.getTransactionalMessageService().prepareMessage(msgInner);
        } else {
          //普通消息,执行普通的消息存储
            putMessageResult = this.brokerController.getMessageStore().putMessage(msgInner);
        }

        return handlePutMessageResult(putMessageResult, response, request, msgInner, responseHeader, sendMessageContext, ctx, queueIdInt);

    }
TAG3.1 brokerController.getTransactionalMessageService().prepareMessage(msgInner)
TransactionalMessageServiceImpl
  
   @Override
    public PutMessageResult prepareMessage(MessageExtBrokerInner messageInner) {
  //存储half消息
        return transactionalMessageBridge.putHalfMessage(messageInner);
    }
TransactionalMessageBridge
  
    public PutMessageResult putHalfMessage(MessageExtBrokerInner messageInner) {
        return store.putMessage(parseHalfMessageInner(messageInner));
    }

//解析half消息
    private MessageExtBrokerInner parseHalfMessageInner(MessageExtBrokerInner msgInner) {
        MessageAccessor.putProperty(msgInner, MessageConst.PROPERTY_REAL_TOPIC, msgInner.getTopic());
        MessageAccessor.putProperty(msgInner, MessageConst.PROPERTY_REAL_QUEUE_ID,
            String.valueOf(msgInner.getQueueId()));
        msgInner.setSysFlag(
            MessageSysFlag.resetTransactionValue(msgInner.getSysFlag(), MessageSysFlag.TRANSACTION_NOT_TYPE));
      //变更消息主题为RMQ_SYS_TRANS_HALF_TOPIC
        msgInner.setTopic(TransactionalMessageUtil.buildHalfTopic());
        msgInner.setQueueId(0);
        msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
        return msgInner;
    }

这里,是备份消息原来的topic、queueId到消息的属性中,然后将主题重新设置为RMQ_SYS_TRANS_HALF_TOPIC,消费队列变更为0.

然后 store.putMessage(parseHalfMessageInner(messageInner)),将消息存储在commitlog中,进而转发到RMQ_SYS_TRANS_HALF_TOPIC对应的消息消费队列。

从上述过程来看,消息未提交之前不会存入原来的主题,也就不能被消费。然后rocketMQ采用定人的单独线程任务,去消费该主题消息,然后再满足特定条件下,恢复消息主题,使得该事务消息被消费。

总结上述的事务发送流程,如下:

image-20230612175917740

3.2.2 提交或回滚事务endTransaction

在事务消息发送过程的步骤,可知:

STEP1 为消息添加prepared、producer-group属性。设置producerGroup目的是,在查询local事务状态时,可以从group中随机选择一个producer即可;
STEP2 调用同步发送,发送消息到rocketMQ;

STEP3 根据sendResult的发送状态,确定本地事务的状态
	CASE1 OK当发送结果成功时,执行本地事务executeLocalTransaction方法,来设置事务执行状态;
	CASE2 如果消息刷新超时,slave不可以,标记事务状态为rollback;
	CASE3 其他情况,事务状态仍旧是unknown
STEP4 结束事务 this.endTransaction();
STEP5 构造事务消息发送的方法返回result;
………………producer端发送endTrans………………
TAG4 endTransaction终结事务

可知,在执行STEP4 endTransaction时,业务事务还没有提交,所以在使用transactionListener.executeLocalTransaction方法记录事务消息状态后,应该返回unknown。而事务消息的commit或者rollback,应该根据消息状态的回查,再确定是否提交。

DefaultMQProducerImpl

 public void endTransaction(
        final SendResult sendResult,
        final LocalTransactionState localTransactionState,
        final Throwable localException) throws RemotingException, MQBrokerException, InterruptedException, UnknownHostException {
        final MessageId id;
        if (sendResult.getOffsetMsgId() != null) {
            id = MessageDecoder.decodeMessageId(sendResult.getOffsetMsgId());
        } else {
            id = MessageDecoder.decodeMessageId(sendResult.getMsgId());
        }
        String transactionId = sendResult.getTransactionId();
  //根据所属的消息队列,获取broker地址信息
        final String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(sendResult.getMessageQueue().getBrokerName());
  //构造EndTransactionRequestHeader的请求
        EndTransactionRequestHeader requestHeader = new EndTransactionRequestHeader();
        requestHeader.setTransactionId(transactionId);
        requestHeader.setCommitLogOffset(id.getOffset());
  //根据本地事务状态,设置发送endTransaction的命令是commit、rollback还是not(不作为)
        switch (localTransactionState) {
            case COMMIT_MESSAGE:
                requestHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_COMMIT_TYPE);
                break;
            case ROLLBACK_MESSAGE:
                requestHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_ROLLBACK_TYPE);
                break;
            case UNKNOW:
                requestHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_NOT_TYPE);
                break;
            default:
                break;
        }

        requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
        requestHeader.setTranStateTableOffset(sendResult.getQueueOffset());
        requestHeader.setMsgId(sendResult.getMsgId());
        String remark = localException != null ? ("executeLocalTransactionBranch exception: " + localException.toString()) : null;
  //producer发送终结事务的信息
        this.mQClientFactory.getMQClientAPIImpl().endTransactionOneway(brokerAddr, requestHeader, remark,
            this.defaultMQProducer.getSendMsgTimeout());
    }
………………broker端处理endTrans………………

image-20230531183151803

broker端处理终结事务的处理器为EndTransactionProcessor

TAG5 EndTransactionProcessor.processRequest处理终结事务请求
EndTransactionProcessor

    @Override
    public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) throws
        RemotingCommandException {
        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
        final EndTransactionRequestHeader requestHeader =
            (EndTransactionRequestHeader)request.decodeCommandCustomHeader(EndTransactionRequestHeader.class);
        LOGGER.info("Transaction request:{}", requestHeader);
        if (BrokerRole.SLAVE == brokerController.getMessageStoreConfig().getBrokerRole()) {
            response.setCode(ResponseCode.SLAVE_NOT_AVAILABLE);
            LOGGER.warn("Message store is slave mode, so end transaction is forbidden. ");
            return response;
        }

        if (requestHeader.getFromTransactionCheck()) {
            switch (requestHeader.getCommitOrRollback()) {
                //not
                case MessageSysFlag.TRANSACTION_NOT_TYPE: {
                    LOGGER.warn("Check producer[{}] transaction state, but it's pending status."
                            + "RequestHeader: {} Remark: {}",
                        RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                        requestHeader.toString(),
                        request.getRemark());
                    return null;
                }


                case MessageSysFlag.TRANSACTION_COMMIT_TYPE: {
                    LOGGER.warn("Check producer[{}] transaction state, the producer commit the message."
                            + "RequestHeader: {} Remark: {}",
                        RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                        requestHeader.toString(),
                        request.getRemark());

                    break;
                }

                case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: {
                    LOGGER.warn("Check producer[{}] transaction state, the producer rollback the message."
                            + "RequestHeader: {} Remark: {}",
                        RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                        requestHeader.toString(),
                        request.getRemark());
                    break;
                }
                default:
                    return null;
            }
        } else {
            switch (requestHeader.getCommitOrRollback()) {
                case MessageSysFlag.TRANSACTION_NOT_TYPE: {
                    LOGGER.warn("The producer[{}] end transaction in sending message,  and it's pending status."
                            + "RequestHeader: {} Remark: {}",
                        RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                        requestHeader.toString(),
                        request.getRemark());
                    return null;
                }

                case MessageSysFlag.TRANSACTION_COMMIT_TYPE: {
                    break;
                }

                case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: {
                    LOGGER.warn("The producer[{}] end transaction in sending message, rollback the message."
                            + "RequestHeader: {} Remark: {}",
                        RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                        requestHeader.toString(),
                        request.getRemark());
                    break;
                }
                default:
                    return null;
            }
        }
        OperationResult result = new OperationResult();
  
        //事务信息为commit的情况
        if (MessageSysFlag.TRANSACTION_COMMIT_TYPE == requestHeader.getCommitOrRollback()) {
          //执行提交事务的逻辑(获取消息的物理偏移量)
            result = this.brokerController.getTransactionalMessageService().commitMessage(requestHeader);
            if (result.getResponseCode() == ResponseCode.SUCCESS) {
              
                RemotingCommand res = checkPrepareMessage(result.getPrepareMessage(), requestHeader);
                if (res.getCode() == ResponseCode.SUCCESS) {
                  //构建新的消息对象,并恢复消息的topic、消费队列
                    MessageExtBrokerInner msgInner = endMessageTransaction(result.getPrepareMessage());
                    msgInner.setSysFlag(MessageSysFlag.resetTransactionValue(msgInner.getSysFlag(), requestHeader.getCommitOrRollback()));
                    msgInner.setQueueOffset(requestHeader.getTranStateTableOffset());
                    msgInner.setPreparedTransactionOffset(requestHeader.getCommitLogOffset());
                    msgInner.setStoreTimestamp(result.getPrepareMessage().getStoreTimestamp());
                  //然后再次发送消息,存储在commitlog中(此时消息主题为原来topic,会被转发到对应的消息消费队列,供消费者消费
                    RemotingCommand sendResult = sendFinalMessage(msgInner);
                    if (sendResult.getCode() == ResponseCode.SUCCESS) {
//如果重新存储成功,会删除prepared消息(此处不是真正删除,而是将prepared消息存储入RMQ_SYS_TRANS_OP_HALF_TOPIC,是为了未处理的事务进行回查时候,确定当前消息是否消费过的依据
                 this.brokerController.getTransactionalMessageService().deletePrepareMessage(result.getPrepareMessage());
                    }
                    return sendResult;
                }
                return res;
            }
        } 
   //事务信息为rollback的情况(不用恢复主题并重新发送到commitlog,直接删除----)
  else if (MessageSysFlag.TRANSACTION_ROLLBACK_TYPE == requestHeader.getCommitOrRollback()) {
            result = this.brokerController.getTransactionalMessageService().rollbackMessage(requestHeader);
            if (result.getResponseCode() == ResponseCode.SUCCESS) {
                RemotingCommand res = checkPrepareMessage(result.getPrepareMessage(), requestHeader);
                if (res.getCode() == ResponseCode.SUCCESS) {
                    this.brokerController.getTransactionalMessageService().deletePrepareMessage(result.getPrepareMessage());
                }
                return res;
            }
        }
  
  //事务信息为unknown的情况
        response.setCode(result.getResponseCode());
        response.setRemark(result.getResponseRemark());
        return response;
    }

上述,逻辑,如下:

case commit情况:
1 构建新的消息对象,并恢复消息的topic、消费队列
2 然后再次发送消息,存储在commitlog中(此时消息主题为原来topic,会被转发到对应的消息消费队列,供消费者消费
3 如果重新存储成功,会删除prepared消息(此处不是真正删除,而是将prepared消息存储入RMQ_SYS_TRANS_OP_HALF_TOPIC,是为了未处理的事务进行回查时候,确定当前消息是否消费过的依据

case rollback情况:
如果rollback的消息,省略恢复原主题并重新send存储到commitlog过程,直接删除在---也同样放入op队列中

case unknown情况:
不做任何处理
posted @ 2023-06-12 18:36  LBJboy  阅读(201)  评论(0编辑  收藏  举报