private void endTransaction( //
      final SendResult sendResult, //
      final LocalTransactionState localTransactionState, //
      final Throwable localException)
      throws RemotingException, MQBrokerException, InterruptedException, UnknownHostException {
    final MessageId id = MessageDecoder.decodeMessageId(sendResult.getMsgId());
    final String addr = RemotingUtil.socketAddress2String(id.getAddress());
    EndTransactionRequestHeader requestHeader = new EndTransactionRequestHeader();
    requestHeader.setCommitLogOffset(id.getOffset());
    switch (localTransactionState) {
      case COMMIT_MESSAGE:
        requestHeader.setCommitOrRollback(MessageSysFlag.TransactionCommitType);
        break;
      case ROLLBACK_MESSAGE:
        requestHeader.setCommitOrRollback(MessageSysFlag.TransactionRollbackType);
        break;
      case UNKNOW:
        requestHeader.setCommitOrRollback(MessageSysFlag.TransactionNotType);
        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;
    this.mQClientFactory
        .getMQClientAPIImpl()
        .endTransactionOneway(
            addr, requestHeader, remark, this.defaultMQProducer.getSendMsgTimeout());
  }
  public TransactionSendResult sendMessageInTransaction(
      final Message msg, final LocalTransactionExecuter tranExecuter, final Object arg)
      throws MQClientException {
    if (null == tranExecuter) {
      throw new MQClientException("tranExecuter is null", null);
    }

    // 第一步,向Broker发送一条Prepared消息
    SendResult sendResult = null;
    msg.putProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
    msg.putProperty(
        MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());
    try {
      sendResult = this.send(msg);
    } catch (Exception e) {
      throw new MQClientException("send message Exception", e);
    }

    // 第二步,回调本地事务
    LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;
    Throwable localException = null;
    try {
      localTransactionState = tranExecuter.executeLocalTransactionBranch(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;
    }

    // 第三步,提交或者回滚Broker端消息
    try {
      this.endTransaction(sendResult, localTransactionState, localException);
    } catch (Exception e) {
      log.warn(
          "local transaction execute "
              + localTransactionState
              + ", but end broker transaction failed",
          e);
    }

    TransactionSendResult transactionSendResult = new TransactionSendResult();
    transactionSendResult.setSendStatus(sendResult.getSendStatus());
    transactionSendResult.setMessageQueue(sendResult.getMessageQueue());
    transactionSendResult.setMsgId(sendResult.getMsgId());
    transactionSendResult.setQueueOffset(sendResult.getQueueOffset());
    transactionSendResult.setLocalTransactionState(localTransactionState);
    return transactionSendResult;
  }