blob: aa81707f4fde9432c14c92e57507fbeec5ee73f2 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ConsumeMsgService.h"
#include "Logging.h"
#include "OffsetStore.h"
#include "RebalanceImpl.h"
#include "UtilAll.h"
namespace rocketmq {
const uint64_t MAX_TIME_CONSUME_CONTINUOUSLY = 60000;
ConsumeMessageOrderlyService::ConsumeMessageOrderlyService(DefaultMQPushConsumerImpl* consumer,
int threadCount,
MQMessageListener* msgListener)
: consumer_(consumer),
message_listener_(msgListener),
consume_executor_("OderlyConsumeService", threadCount, false),
scheduled_executor_service_(false) {}
ConsumeMessageOrderlyService::~ConsumeMessageOrderlyService() = default;
void ConsumeMessageOrderlyService::start() {
consume_executor_.startup();
scheduled_executor_service_.startup();
scheduled_executor_service_.schedule(std::bind(&ConsumeMessageOrderlyService::lockMQPeriodically, this),
ProcessQueue::REBALANCE_LOCK_INTERVAL, time_unit::milliseconds);
}
void ConsumeMessageOrderlyService::shutdown() {
stopThreadPool();
unlockAllMQ();
}
void ConsumeMessageOrderlyService::stopThreadPool() {
consume_executor_.shutdown();
scheduled_executor_service_.shutdown();
}
void ConsumeMessageOrderlyService::lockMQPeriodically() {
consumer_->getRebalanceImpl()->lockAll();
scheduled_executor_service_.schedule(std::bind(&ConsumeMessageOrderlyService::lockMQPeriodically, this),
ProcessQueue::REBALANCE_LOCK_INTERVAL, time_unit::milliseconds);
}
void ConsumeMessageOrderlyService::unlockAllMQ() {
consumer_->getRebalanceImpl()->unlockAll(false);
}
bool ConsumeMessageOrderlyService::lockOneMQ(const MQMessageQueue& mq) {
return consumer_->getRebalanceImpl()->lock(mq);
}
void ConsumeMessageOrderlyService::submitConsumeRequest(std::vector<MessageExtPtr>& msgs,
ProcessQueuePtr processQueue,
const MQMessageQueue& messageQueue,
const bool dispathToConsume) {
if (dispathToConsume) {
consume_executor_.submit(
std::bind(&ConsumeMessageOrderlyService::ConsumeRequest, this, processQueue, messageQueue));
}
}
void ConsumeMessageOrderlyService::submitConsumeRequestLater(ProcessQueuePtr processQueue,
const MQMessageQueue& messageQueue,
const long suspendTimeMillis) {
long timeMillis = suspendTimeMillis;
if (timeMillis == -1) {
timeMillis = 1000;
}
timeMillis = std::max(10L, std::min(timeMillis, 30000L));
static std::vector<MessageExtPtr> dummy;
scheduled_executor_service_.schedule(std::bind(&ConsumeMessageOrderlyService::submitConsumeRequest, this,
std::ref(dummy), processQueue, messageQueue, true),
timeMillis, time_unit::milliseconds);
}
void ConsumeMessageOrderlyService::tryLockLaterAndReconsume(const MQMessageQueue& mq,
ProcessQueuePtr processQueue,
const long delayMills) {
scheduled_executor_service_.schedule(
[this, mq, processQueue]() {
bool lockOK = lockOneMQ(mq);
if (lockOK) {
submitConsumeRequestLater(processQueue, mq, 10);
} else {
submitConsumeRequestLater(processQueue, mq, 3000);
}
},
delayMills, time_unit::milliseconds);
}
void ConsumeMessageOrderlyService::ConsumeRequest(ProcessQueuePtr processQueue, const MQMessageQueue& messageQueue) {
if (processQueue->dropped()) {
LOG_WARN_NEW("run, the message queue not be able to consume, because it's dropped. {}", messageQueue.toString());
return;
}
auto objLock = message_queue_lock_.fetchLockObject(messageQueue);
std::lock_guard<std::mutex> lock(*objLock);
if (BROADCASTING == consumer_->messageModel() || (processQueue->locked() && !processQueue->isLockExpired())) {
auto beginTime = UtilAll::currentTimeMillis();
for (bool continueConsume = true; continueConsume;) {
if (processQueue->dropped()) {
LOG_WARN_NEW("the message queue not be able to consume, because it's dropped. {}", messageQueue.toString());
break;
}
if (CLUSTERING == consumer_->messageModel() && !processQueue->locked()) {
LOG_WARN_NEW("the message queue not locked, so consume later, {}", messageQueue.toString());
tryLockLaterAndReconsume(messageQueue, processQueue, 10);
break;
}
if (CLUSTERING == consumer_->messageModel() && processQueue->isLockExpired()) {
LOG_WARN_NEW("the message queue lock expired, so consume later, {}", messageQueue.toString());
tryLockLaterAndReconsume(messageQueue, processQueue, 10);
break;
}
auto interval = UtilAll::currentTimeMillis() - beginTime;
if (interval > MAX_TIME_CONSUME_CONTINUOUSLY) {
submitConsumeRequestLater(processQueue, messageQueue, 10);
break;
}
const int consumeBatchSize = consumer_->getDefaultMQPushConsumerConfig()->consume_message_batch_max_size();
std::vector<MessageExtPtr> msgs;
processQueue->takeMessages(msgs, consumeBatchSize);
consumer_->resetRetryAndNamespace(msgs);
if (!msgs.empty()) {
ConsumeStatus status = RECONSUME_LATER;
try {
std::lock_guard<std::timed_mutex> lock(processQueue->lock_consume());
if (processQueue->dropped()) {
LOG_WARN_NEW("consumeMessage, the message queue not be able to consume, because it's dropped. {}",
messageQueue.toString());
break;
}
auto message_list = MQMessageExt::from_list(msgs);
status = message_listener_->consumeMessage(message_list);
} catch (const std::exception& e) {
LOG_WARN_NEW("encounter unexpected exception when consume messages.\n{}", e.what());
}
// processConsumeResult
long commitOffset = -1L;
switch (status) {
case CONSUME_SUCCESS:
commitOffset = processQueue->commit();
break;
case RECONSUME_LATER:
processQueue->makeMessageToCosumeAgain(msgs);
submitConsumeRequestLater(processQueue, messageQueue, -1);
continueConsume = false;
break;
default:
break;
}
if (commitOffset >= 0 && !processQueue->dropped()) {
consumer_->getOffsetStore()->updateOffset(messageQueue, commitOffset, false);
}
} else {
continueConsume = false;
}
}
} else {
if (processQueue->dropped()) {
LOG_WARN_NEW("the message queue not be able to consume, because it's dropped. {}", messageQueue.toString());
return;
}
tryLockLaterAndReconsume(messageQueue, processQueue, 100);
}
}
} // namespace rocketmq