• RocketMQ-事务及延迟消息


     RocketMQ事务支持

    整体流程

    实现细节

     下面的代码是producer完成1-4的步骤:

        public TransactionSendResult sendMessageInTransaction(final Message msg, final LocalTransactionExecuter tranExecuter, final Object arg)
            throws MQClientException {
            if (null == tranExecuter) {
                throw new MQClientException("tranExecutor is null", null);
            }
            // 数据校验
            Validators.checkMessage(msg, this.defaultMQProducer);
    
            SendResult sendResult = null;
            /**
             * 消息预处理
             */
            // 设置当前是事务消息
            MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
            MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());
            try {
                //发送half消息
                sendResult = this.send(msg);
            } catch (Exception e) {
                throw new MQClientException("send message Exception", e);
            }
    
            LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;
            Throwable localException = null;
            switch (sendResult.getSendStatus()) {
                case SEND_OK: {
                    try {
                        if (sendResult.getTransactionId() != null) {
                            msg.putUserProperty("__transactionId__", sendResult.getTransactionId());
                        }
                        // 执行本地事务
                        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;
                    }
                }
                break;
                case FLUSH_DISK_TIMEOUT:
                case FLUSH_SLAVE_TIMEOUT:
                case SLAVE_NOT_AVAILABLE:
                    localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;
                    break;
                default:
                    break;
            }
    
            try {
                // 发送commit或者rollback消息
                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.setTransactionId(sendResult.getTransactionId());
            transactionSendResult.setLocalTransactionState(localTransactionState);
            return transactionSendResult;
        }

    接下来看一下broker端是如何处理的,broker处理producer的half事务消息是使用SendMessageProcessor,其中有下面的代码片段,后续的具体处理逻辑在TransactionMessageService中,而具体实现依赖  TransactionalMessageBridge

    String transFlag = origProps.get(MessageConst.PROPERTY_TRANSACTION_PREPARED);
            if (transFlag != null && Boolean.parseBoolean(transFlag)) {
                if (this.brokerController.getBrokerConfig().isRejectTransactionMessage()) {
                    response.setCode(ResponseCode.NO_PERMISSION);
                    response.setRemark(
                            "the broker[" + this.brokerController.getBrokerConfig().getBrokerIP1()
                                    + "] sending transaction message is forbidden");
                    return CompletableFuture.completedFuture(response);
                }
                // 处理事务half消息
                putMessageResult = this.brokerController.getTransactionalMessageService().asyncPrepareMessage(msgInner);
            } else {
                // 处理普通的消息
                putMessageResult = this.brokerController.getMessageStore().asyncPutMessage(msgInner);
            }

    接下来会把half消息的topic和queueid备份并替换系统topic和queueid=0,并写到commitlog中

        private MessageExtBrokerInner parseHalfMessageInner(MessageExtBrokerInner msgInner) {
            // 备份当前的topic 和 queueId
            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));
            // 设置当前的topic为 RMQ_SYS_TRANS_HALF_TOPIC
            msgInner.setTopic(TransactionalMessageUtil.buildHalfTopic());
            msgInner.setQueueId(0);
            msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
            return msgInner;
        }

    producer在接下来会执行本地事务,并发送commit或者rollback消息给broker,broker使用EndTransactionProcessor来处理

            OperationResult result = new OperationResult();
            // 请求commit操作
            if (MessageSysFlag.TRANSACTION_COMMIT_TYPE == requestHeader.getCommitOrRollback()) {
                // 查询到half消息
                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());
                        MessageAccessor.clearProperty(msgInner, MessageConst.PROPERTY_TRANSACTION_PREPARED);
                        // 构建响应,恢复出一条完整的普通消息,然后走一遍消息写入流程
                        RemotingCommand sendResult = sendFinalMessage(msgInner);
                        if (sendResult.getCode() == ResponseCode.SUCCESS) {
                            /**
                             *  删除事务消息  用于half消息回查,实际上并不是做真正的物理删除,而是根据half对列来匹配到OP对列
                             *  并将commit消息写到OP队列,tagcode为remove标志
                             */
    
                            this.brokerController.getTransactionalMessageService().deletePrepareMessage(result.getPrepareMessage());
                        }
                        return sendResult;
                    }
                    return res;
                }
            } 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) {
                        // 与commit操作相同逻辑   区别在于不在生成一个普通的message 让消费者消费
                        this.brokerController.getTransactionalMessageService().deletePrepareMessage(result.getPrepareMessage());
                    }
                    return res;
                }
            }

    事务消息回查

    在TransactionalMessageCheckService中,每隔1分钟就会进行回查一次

     protected void onWaitEnd() {
            // 事务消息超时时间
            long timeout = brokerController.getBrokerConfig().getTransactionTimeOut();
            // 最大回查次数
            int checkMax = brokerController.getBrokerConfig().getTransactionCheckMax();
            long begin = System.currentTimeMillis();
            log.info("Begin to check prepare message, begin time:{}", begin);
            this.brokerController.getTransactionalMessageService().check(timeout, checkMax, this.brokerController.getTransactionalMessageCheckListener());
            log.info("End to check prepare message, consumed time:{}", System.currentTimeMillis() - begin);
        }
     @Override
        public void check(long transactionTimeout, int transactionCheckMax,
            AbstractTransactionalMessageCheckListener listener) {
            try {
                String topic = TopicValidator.RMQ_SYS_TRANS_HALF_TOPIC;
                Set<MessageQueue> msgQueues = transactionalMessageBridge.fetchMessageQueues(topic);
                if (msgQueues == null || msgQueues.size() == 0) {
                    log.warn("The queue of topic is empty :" + topic);
                    return;
                }
                log.debug("Check topic={}, queues={}", topic, msgQueues);
                //  遍历half的messagequeue
                for (MessageQueue messageQueue : msgQueues) {
                    long startTime = System.currentTimeMillis();
                    /**
                     * 根据half的messagequeue  获取op的messagequeue
                     */
                    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;
                    }
    
                    List<Long> doneOpOffset = new ArrayList<>();
                    // key:halfOffset, value: opOffset.  存储已经执行完OP后,的点位信息
                    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;
                    }
                    // single thread
                    int getMessageNullCount = 1;
                    long newOffset = halfOffset;
                    long i = halfOffset;
                    while (true) {
                        if (System.currentTimeMillis() - startTime > MAX_PROCESS_TIME_LIMIT) {
                            log.info("Queue={} process time reach max={}", messageQueue, MAX_PROCESS_TIME_LIMIT);
                            break;
                        }
                        // 当前的half已经op过了 
                        if (removeMap.containsKey(i)) {
                            log.debug("Half offset {} has been committed/rolled back", i);
                            Long removedOpOffset = removeMap.remove(i);
                            doneOpOffset.add(removedOpOffset);
                        } else {
                            GetResult getResult = getHalfMsg(messageQueue, i);
                            MessageExt msgExt = getResult.getMsg();
                            if (msgExt == null) {
                                if (getMessageNullCount++ > MAX_RETRY_COUNT_WHEN_HALF_NULL) {
                                    break;
                                }
                                if (getResult.getPullResult().getPullStatus() == PullStatus.NO_NEW_MSG) {
                                    log.debug("No new msg, the miss offset={} in={}, continue check={}, pull result={}", i,
                                        messageQueue, getMessageNullCount, getResult.getPullResult());
                                    break;
                                } else {
                                    log.info("Illegal offset, the miss offset={} in={}, continue check={}, pull result={}",
                                        i, messageQueue, getMessageNullCount, getResult.getPullResult());
                                    i = getResult.getPullResult().getNextBeginOffset();
                                    newOffset = i;
                                    continue;
                                }
                            }
                            // 超过一定次数的回调  就丢弃
                            if (needDiscard(msgExt, transactionCheckMax) || needSkip(msgExt)) {
                                listener.resolveDiscardMsg(msgExt);
                                newOffset = i + 1;
                                i++;
                                continue;
                            }
                            // 新到达的half以后处理
                            if (msgExt.getStoreTimestamp() >= startTime) {
                                log.debug("Fresh stored. the miss offset={}, check it later, store={}", i,
                                    new Date(msgExt.getStoreTimestamp()));
                                break;
                            }
    
                            long valueOfCurrentMinusBorn = System.currentTimeMillis() - msgExt.getBornTimestamp();
                            // 消息回查免疫时间
                            long checkImmunityTime = transactionTimeout;
                            String checkImmunityTimeStr = msgExt.getUserProperty(MessageConst.PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS);
                            if (null != checkImmunityTimeStr) {
                                checkImmunityTime = getImmunityTime(checkImmunityTimeStr, transactionTimeout);
                                if (valueOfCurrentMinusBorn < checkImmunityTime) {  // 免疫时间内跳过
                                    if (checkPrepareQueueOffset(removeMap, doneOpOffset, msgExt)) {
                                        newOffset = i + 1;
                                        i++;
                                        continue;
                                    }
                                }
                            } else {  
                                if ((0 <= valueOfCurrentMinusBorn) && (valueOfCurrentMinusBorn < checkImmunityTime)) {
                                    log.debug("New arrived, the miss offset={}, check it later checkImmunity={}, born={}", i,
                                        checkImmunityTime, new Date(msgExt.getBornTimestamp()));
                                    break;
                                }
                            }
                            List<MessageExt> opMsg = pullResult.getMsgFoundList();
                            /**
                             * 回查条件:
                             * 如果没有OP消息,并且当前的half消息在免疫期外
                             * 当前Half存在OP消息,并且最后一个本批次OP消息中的最后一个消息在免疫期外
                             * broker和客户端有时间差
                             */
                            boolean isNeedCheck = (opMsg == null && valueOfCurrentMinusBorn > checkImmunityTime)
                                || (opMsg != null && (opMsg.get(opMsg.size() - 1).getBornTimestamp() - startTime > transactionTimeout))
                                || (valueOfCurrentMinusBorn <= -1);
    
                            if (isNeedCheck) {
                                /**
                                 * 因为回查的时候 不确定是否回查成功,所以还是会把当前的half信息在half队列在写一遍
                                 */
                                if (!putBackHalfMsgQueue(msgExt, i)) {
                                    continue;
                                }
                                // 异步回查
                                listener.resolveHalfMsg(msgExt);
                            } else {
                                pullResult = fillOpRemoveMap(removeMap, opQueue, pullResult.getNextBeginOffset(), halfOffset, doneOpOffset);
                                log.debug("The miss offset:{} in messageQueue:{} need to get more opMsg, result is:{}", i,
                                    messageQueue, pullResult);
                                continue;
                            }
                        }
                        newOffset = i + 1;
                        i++;
                    }
                    if (newOffset != halfOffset) {
                        transactionalMessageBridge.updateConsumeOffset(messageQueue, newOffset);
                    }
                    long newOpOffset = calculateOpOffset(doneOpOffset, opOffset);
                    if (newOpOffset != opOffset) {
                        transactionalMessageBridge.updateConsumeOffset(opQueue, newOpOffset);
                    }
                }
            } catch (Throwable e) {
                log.error("Check error", e);
            }
    
        }

    延迟消息

    参考 https://blog.csdn.net/hosaos/article/details/90577732

    Rocketmq在存储延迟消息的时候,将其保存在一个系统的topic中,在创建consume queue的时候,tag code字段保存这延迟消息需要被投递的时间,通过这个存储思路

    ,总结一下延迟消息的投递过程:通过定时服务扫描consume queue,满足投递的时间条件的消息再通过查询commitlog 将消息重新投递到原始的topic,消费者就可以接收消息了。

    延迟消息写入

    1 在commitlog的putmessage有这么一段,确定延迟级别并且备份原来的topic,换成系统topic    RMQ_SYS_SCHEDULE_TOPIC

     // Delay Delivery
                if (msg.getDelayTimeLevel() > 0) {
                    if (msg.getDelayTimeLevel() > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) {
                        msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel());
                    }
    
                    topic = TopicValidator.RMQ_SYS_SCHEDULE_TOPIC;
                    queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel());
    
                    // Backup real topic, queueId
                    MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_TOPIC, msg.getTopic());
                    MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_QUEUE_ID, String.valueOf(msg.getQueueId()));
                    msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));
    
                    msg.setTopic(topic);
                    msg.setQueueId(queueId);
                }

    在reputservice 里面 doReput的时候执行

    DispatchRequest dispatchRequest =DefaultMessageStore.this.commitLog.checkMessageAndReturnSize(result.getByteBuffer(), false, false);

    内部对延迟消息的tagcode为推送时间

    // Timing message processing
                    {
                        String t = propertiesMap.get(MessageConst.PROPERTY_DELAY_TIME_LEVEL);
                        if (TopicValidator.RMQ_SYS_SCHEDULE_TOPIC.equals(topic) && t != null) {
                            int delayLevel = Integer.parseInt(t);
    
                            if (delayLevel > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) {
                                delayLevel = this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel();
                            }
    
                            if (delayLevel > 0) {
                                tagsCode = this.defaultMessageStore.getScheduleMessageService().computeDeliverTimestamp(delayLevel,
                                    storeTimestamp);
                            }
                        }
                    }
        public long computeDeliverTimestamp(final int delayLevel, final long storeTimestamp) {
            Long time = this.delayLevelTable.get(delayLevel);
            if (time != null) {
                return time + storeTimestamp;
            }
    
            return storeTimestamp + 1000;
        }


    接下来就是ScheduleMessageService定时处理了。每一个延迟级别的consumequeue,在刚开始都会有一个对应timer在10s后执行。然后每一个级别的队列中找到上次处理过的点位之后的延迟消息,遍历之后,如果发现到点了,则写到普通topic队列中,如果没有到点,则把距离到点的时间差作为下次执行调度的延迟时间。并return出去。

        public void start() {
            if (started.compareAndSet(false, true)) {
                super.load();
                this.timer = new Timer("ScheduleMessageTimerThread", true);
                for (Map.Entry<Integer, Long> entry : this.delayLevelTable.entrySet()) {
                    Integer level = entry.getKey();
                    Long timeDelay = entry.getValue();
                    Long offset = this.offsetTable.get(level);
                    if (null == offset) {
                        offset = 0L;
                    }
    
                    // 每一个延迟队列都会起一个定时任务  第一次10s后执行
                    if (timeDelay != null) {
                        this.timer.schedule(new DeliverDelayedMessageTimerTask(level, offset), FIRST_DELAY_TIME);
                    }
                }
    
                // 每10s持久化
                this.timer.scheduleAtFixedRate(new TimerTask() {
    
                    @Override
                    public void run() {
                        try {
                            if (started.get()) ScheduleMessageService.this.persist();
                        } catch (Throwable e) {
                            log.error("scheduleAtFixedRate flush exception", e);
                        }
                    }
                }, 10000, this.defaultMessageStore.getMessageStoreConfig().getFlushDelayOffsetInterval());
            }
        }

     下面是具体的延迟消息处理逻辑

            public void executeOnTimeup() {
                ConsumeQueue cq =
                    ScheduleMessageService.this.defaultMessageStore.findConsumeQueue(TopicValidator.RMQ_SYS_SCHEDULE_TOPIC,
                        delayLevel2QueueId(delayLevel));
    
                long failScheduleOffset = offset;
    
                if (cq != null) {
                    SelectMappedBufferResult bufferCQ = cq.getIndexBuffer(this.offset);
                    if (bufferCQ != null) {
                        try {
                            long nextOffset = offset;
                            int i = 0;
                            ConsumeQueueExt.CqExtUnit cqExtUnit = new ConsumeQueueExt.CqExtUnit();
                            for (; i < bufferCQ.getSize(); i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
                                long offsetPy = bufferCQ.getByteBuffer().getLong();
                                int sizePy = bufferCQ.getByteBuffer().getInt();
                                long tagsCode = bufferCQ.getByteBuffer().getLong();
    
                                if (cq.isExtAddr(tagsCode)) {
                                    if (cq.getExt(tagsCode, cqExtUnit)) {
                                        tagsCode = cqExtUnit.getTagsCode();
                                    } else {
                                        //can't find ext content.So re compute tags code.
                                        log.error("[BUG] can't find consume queue extend file content!addr={}, offsetPy={}, sizePy={}",
                                            tagsCode, offsetPy, sizePy);
                                        long msgStoreTime = defaultMessageStore.getCommitLog().pickupStoreTimestamp(offsetPy, sizePy);
                                        tagsCode = computeDeliverTimestamp(delayLevel, msgStoreTime);
                                    }
                                }
    
                                long now = System.currentTimeMillis();
                                /**
                                 * 计算推送时间错
                                 */
                                long deliverTimestamp = this.correctDeliverTimestamp(now, tagsCode);
    
                                nextOffset = offset + (i / ConsumeQueue.CQ_STORE_UNIT_SIZE);
    
                                long countdown = deliverTimestamp - now;
    
                                // 到了触发的时间点
                                if (countdown <= 0) {
                                    MessageExt msgExt =
                                        ScheduleMessageService.this.defaultMessageStore.lookMessageByOffset(
                                            offsetPy, sizePy);
    
                                    if (msgExt != null) {
                                        try {
                                            // 恢复topic
                                            MessageExtBrokerInner msgInner = this.messageTimeup(msgExt);
                                            if (TopicValidator.RMQ_SYS_TRANS_HALF_TOPIC.equals(msgInner.getTopic())) {
                                                log.error("[BUG] the real topic of schedule msg is {}, discard the msg. msg={}",
                                                        msgInner.getTopic(), msgInner);
                                                continue;
                                            }
                                            // 消息写入普通topic队列
                                            PutMessageResult putMessageResult =
                                                ScheduleMessageService.this.writeMessageStore
                                                    .putMessage(msgInner);
    
                                            if (putMessageResult != null
                                                && putMessageResult.getPutMessageStatus() == PutMessageStatus.PUT_OK) {
                                                if (ScheduleMessageService.this.defaultMessageStore.getMessageStoreConfig().isEnableScheduleMessageStats()) {
                                                    ScheduleMessageService.this.defaultMessageStore.getBrokerStatsManager().incTopicPutNums(msgInner.getTopic(), putMessageResult.getAppendMessageResult().getMsgNum(), 1);
                                                    ScheduleMessageService.this.defaultMessageStore.getBrokerStatsManager().incTopicPutSize(msgInner.getTopic(),
                                                        putMessageResult.getAppendMessageResult().getWroteBytes());
                                                    ScheduleMessageService.this.defaultMessageStore.getBrokerStatsManager().incBrokerPutNums(putMessageResult.getAppendMessageResult().getMsgNum());
                                                }
                                                continue;
                                            } else {
                                                // XXX: warn and notify me
                                                log.error(
                                                    "ScheduleMessageService, a message time up, but reput it failed, topic: {} msgId {}",
                                                    msgExt.getTopic(), msgExt.getMsgId());
                                                ScheduleMessageService.this.timer.schedule(
                                                    new DeliverDelayedMessageTimerTask(this.delayLevel,
                                                        nextOffset), DELAY_FOR_A_PERIOD);
                                                ScheduleMessageService.this.updateOffset(this.delayLevel,
                                                    nextOffset);
                                                return;
                                            }
                                        } catch (Exception e) {
                                            /*
                                             * XXX: warn and notify me
    
    
    
                                             */
                                            log.error(
                                                "ScheduleMessageService, messageTimeup execute error, drop it. msgExt="
                                                    + msgExt + ", nextOffset=" + nextOffset + ",offsetPy="
                                                    + offsetPy + ",sizePy=" + sizePy, e);
                                        }
                                    }
                                } else {
                                    // 没有到时间点  将到点的时间差作为调度延迟时间
                                    ScheduleMessageService.this.timer.schedule(
                                        new DeliverDelayedMessageTimerTask(this.delayLevel, nextOffset),
                                        countdown);
                                    ScheduleMessageService.this.updateOffset(this.delayLevel, nextOffset);
                                    return;
                                }
                            } // end of for
    
                            nextOffset = offset + (i / ConsumeQueue.CQ_STORE_UNIT_SIZE);
                            ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(
                                this.delayLevel, nextOffset), DELAY_FOR_A_WHILE);
                            ScheduleMessageService.this.updateOffset(this.delayLevel, nextOffset);
                            return;
                        } finally {
    
                            bufferCQ.release();
                        }
                    } // end of if (bufferCQ != null)
                    else {
    
                        long cqMinOffset = cq.getMinOffsetInQueue();
                        if (offset < cqMinOffset) {
                            failScheduleOffset = cqMinOffset;
                            log.error("schedule CQ offset invalid. offset=" + offset + ", cqMinOffset="
                                + cqMinOffset + ", queueId=" + cq.getQueueId());
                        }
                    }
                } // end of if (cq != null)
    
                // 当全部执行完  或者 没有延迟消息的时候   保持一个调度  去check队列
                ScheduleMessageService.this.timer.schedule(new DeliverDelayedMessageTimerTask(this.delayLevel,
                    failScheduleOffset), DELAY_FOR_A_WHILE);
            }

    参考https://www.cnblogs.com/hzmark/p/rocket_txn.html

    事务消息和延迟消息在rocketmq4.2.0版本中并不支持,在4.3.0后支持

  • 相关阅读:
    java.math.BigDecimal
    《大话设计模式 Java溢彩加强版》相关主题
    js获取不到vfor里面的元素
    org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'null' in 'class com.
    [Typescript] 100. Hard IsAny
    [Typescript] 98. Medium Append to object
    [Typescript] 103. Hard Tuple Filter
    [Javascript] Trampolines & Tail call
    [Typescript] 97. Hard Capitalize Words
    [Typescript] 102. Hard String to Number
  • 原文地址:https://www.cnblogs.com/gaojy/p/15077743.html
Copyright © 2020-2023  润新知