blob: efdc1cc345ff74f3ff5ee678998cec1a847cb91b [file]
/**
* Copyright (C) 2013 kangliqiang ,kangliq@163.com
*
* Licensed 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 "RebalanceImpl.h"
#include "AllocateMessageQueueStrategy.h"
#include "MQClientFactory.h"
#include "MixAll.h"
#include "LockBatchBody.h"
#include "MQClientAPIImpl.h"
#include "KPRUtil.h"
#include "ScopedLock.h"
namespace rmq
{
RebalanceImpl::RebalanceImpl(const std::string& consumerGroup,
MessageModel messageModel,
AllocateMessageQueueStrategy* pAllocateMessageQueueStrategy,
MQClientFactory* pMQClientFactory)
: m_consumerGroup(consumerGroup),
m_messageModel(messageModel),
m_pAllocateMessageQueueStrategy(pAllocateMessageQueueStrategy),
m_pMQClientFactory(pMQClientFactory)
{
}
RebalanceImpl::~RebalanceImpl()
{
}
void RebalanceImpl::unlock(MessageQueue& mq, bool oneway)
{
FindBrokerResult findBrokerResult =
m_pMQClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), MixAll::MASTER_ID, true);
if (!findBrokerResult.brokerAddr.empty())
{
UnlockBatchRequestBody* requestBody = new UnlockBatchRequestBody();
requestBody->setConsumerGroup(m_consumerGroup);
requestBody->setClientId(m_pMQClientFactory->getClientId());
requestBody->getMqSet().insert(mq);
try
{
m_pMQClientFactory->getMQClientAPIImpl()->unlockBatchMQ(findBrokerResult.brokerAddr,
requestBody, 1000, oneway);
}
catch (...)
{
RMQ_ERROR("unlockBatchMQ exception, MQ: {%s}" , mq.toString().c_str());
}
}
}
void RebalanceImpl::unlockAll(bool oneway)
{
std::map<std::string, std::set<MessageQueue> > brokerMqs = buildProcessQueueTableByBrokerName();
std::map<std::string, std::set<MessageQueue> >::iterator it = brokerMqs.begin();
for (; it != brokerMqs.end(); it++)
{
std::string brokerName = it->first;
std::set<MessageQueue> mqs = it->second;
if (mqs.empty())
{
continue;
}
FindBrokerResult findBrokerResult =
m_pMQClientFactory->findBrokerAddressInSubscribe(brokerName, MixAll::MASTER_ID, true);
if (!findBrokerResult.brokerAddr.empty())
{
UnlockBatchRequestBody* requestBody = new UnlockBatchRequestBody();
requestBody->setConsumerGroup(m_consumerGroup);
requestBody->setClientId(m_pMQClientFactory->getClientId());
requestBody->setMqSet(mqs);
try
{
m_pMQClientFactory->getMQClientAPIImpl()->unlockBatchMQ(findBrokerResult.brokerAddr,
requestBody, 1000, oneway);
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::set<MessageQueue>::iterator itm = mqs.begin();
for (; itm != mqs.end(); itm++)
{
std::map<MessageQueue, ProcessQueue*>::iterator itp = m_processQueueTable.find(*itm);
if (itp != m_processQueueTable.end())
{
itp->second->setLocked(false);
RMQ_INFO("the message queue unlock OK, Group: {%s}, MQ: {%s}",
m_consumerGroup.c_str(), (*itm).toString().c_str());
}
}
}
catch (...)
{
RMQ_ERROR("unlockBatchMQ exception, mqs.size: {%u} ", (unsigned)mqs.size());
}
}
}
}
bool RebalanceImpl::lock(MessageQueue& mq)
{
FindBrokerResult findBrokerResult =
m_pMQClientFactory->findBrokerAddressInSubscribe(mq.getBrokerName(), MixAll::MASTER_ID, true);
if (!findBrokerResult.brokerAddr.empty())
{
LockBatchRequestBody* requestBody = new LockBatchRequestBody();
requestBody->setConsumerGroup(m_consumerGroup);
requestBody->setClientId(m_pMQClientFactory->getClientId());
requestBody->getMqSet().insert(mq);
try
{
std::set<MessageQueue> lockedMq =
m_pMQClientFactory->getMQClientAPIImpl()->lockBatchMQ(
findBrokerResult.brokerAddr, requestBody, 1000);
std::set<MessageQueue>::iterator it = lockedMq.begin();
for (; it != lockedMq.end(); it++)
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
MessageQueue mmqq = *it;
std::map<MessageQueue, ProcessQueue*>::iterator itt = m_processQueueTable.find(mmqq);
if (itt != m_processQueueTable.end())
{
itt->second->setLocked(true);
itt->second->setLastLockTimestamp(KPRUtil::GetCurrentTimeMillis());
}
}
it = lockedMq.find(mq);
bool lockOK = (it != lockedMq.end());
RMQ_INFO("the message queue lock {%s}, {%s}, {%s}",//
(lockOK ? "OK" : "Failed"), //
m_consumerGroup.c_str(), //
mq.toString().c_str());
return lockOK;
}
catch (...)
{
RMQ_ERROR("lockBatchMQ exception, MQ: {%s}", mq.toString().c_str());
}
}
return false;
}
void RebalanceImpl::lockAll()
{
std::map<std::string, std::set<MessageQueue> > brokerMqs = buildProcessQueueTableByBrokerName();
std::map<std::string, std::set<MessageQueue> >::iterator it = brokerMqs.begin();
for (; it != brokerMqs.end(); it++)
{
std::string brokerName = it->first;
std::set<MessageQueue> mqs = it->second;
if (mqs.empty())
{
continue;
}
FindBrokerResult findBrokerResult =
m_pMQClientFactory->findBrokerAddressInSubscribe(brokerName, MixAll::MASTER_ID, true);
if (!findBrokerResult.brokerAddr.empty())
{
LockBatchRequestBody* requestBody = new LockBatchRequestBody();
requestBody->setConsumerGroup(m_consumerGroup);
requestBody->setClientId(m_pMQClientFactory->getClientId());
requestBody->setMqSet(mqs);
try
{
std::set<MessageQueue> lockOKMQSet =
m_pMQClientFactory->getMQClientAPIImpl()->lockBatchMQ(
findBrokerResult.brokerAddr, requestBody, 1000);
std::set<MessageQueue>::iterator its = lockOKMQSet.begin();
for (; its != lockOKMQSet.end(); its++)
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
MessageQueue mq = *its;
std::map<MessageQueue, ProcessQueue*>::iterator itt = m_processQueueTable.find(mq);
if (itt != m_processQueueTable.end())
{
ProcessQueue* processQueue = itt->second;
if (!processQueue->isLocked())
{
RMQ_INFO("the message queue locked OK, Group: {%s}, MQ: %s",
m_consumerGroup.c_str(),
mq.toString().c_str());
}
processQueue->setLocked(true);
processQueue->setLastLockTimestamp(KPRUtil::GetCurrentTimeMillis());
}
}
its = mqs.begin();
for (; its != mqs.end(); its++)
{
MessageQueue mq = *its;
std::set<MessageQueue>::iterator itf = lockOKMQSet.find(mq);
if (itf == lockOKMQSet.end())
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::map<MessageQueue, ProcessQueue*>::iterator itt = m_processQueueTable.find(mq);
if (itt != m_processQueueTable.end())
{
itt->second->setLocked(false);
RMQ_WARN("the message queue locked Failed, Group: {%s}, MQ: %s",
m_consumerGroup.c_str(),
mq.toString().c_str());
}
}
}
}
catch (std::exception& e)
{
RMQ_ERROR("lockBatchMQ exception: %s", e.what());
}
}
}
}
void RebalanceImpl::doRebalance()
{
std::map<std::string, SubscriptionData> subTable = getSubscriptionInner();
std::map<std::string, SubscriptionData>::iterator it = subTable.begin();
for (; it != subTable.end(); it++)
{
std::string topic = it->first;
try
{
rebalanceByTopic(topic);
}
catch (std::exception& e)
{
if (topic.find(MixAll::RETRY_GROUP_TOPIC_PREFIX) != 0)
{
RMQ_WARN("rebalanceByTopic Exception: %s", e.what());
}
}
}
truncateMessageQueueNotMyTopic();
}
std::map<std::string, SubscriptionData>& RebalanceImpl::getSubscriptionInner()
{
return m_subscriptionInner;
}
std::map<MessageQueue, ProcessQueue*>& RebalanceImpl::getProcessQueueTable()
{
return m_processQueueTable;
}
kpr::RWMutex& RebalanceImpl::getProcessQueueTableLock()
{
return m_processQueueTableLock;
}
std::map<std::string, std::set<MessageQueue> >& RebalanceImpl::getTopicSubscribeInfoTable()
{
return m_topicSubscribeInfoTable;
}
std::string& RebalanceImpl::getConsumerGroup()
{
return m_consumerGroup;
}
void RebalanceImpl::setConsumerGroup(const std::string& consumerGroup)
{
m_consumerGroup = consumerGroup;
}
MessageModel RebalanceImpl::getMessageModel()
{
return m_messageModel;
}
void RebalanceImpl::setMessageModel(MessageModel messageModel)
{
m_messageModel = messageModel;
}
AllocateMessageQueueStrategy* RebalanceImpl::getAllocateMessageQueueStrategy()
{
return m_pAllocateMessageQueueStrategy;
}
void RebalanceImpl::setAllocateMessageQueueStrategy(AllocateMessageQueueStrategy* pAllocateMessageQueueStrategy)
{
m_pAllocateMessageQueueStrategy = pAllocateMessageQueueStrategy;
}
MQClientFactory* RebalanceImpl::getmQClientFactory()
{
return m_pMQClientFactory;
}
void RebalanceImpl::setmQClientFactory(MQClientFactory* pMQClientFactory)
{
m_pMQClientFactory = pMQClientFactory;
}
std::map<std::string, std::set<MessageQueue> > RebalanceImpl::buildProcessQueueTableByBrokerName()
{
std::map<std::string, std::set<MessageQueue> > result ;
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::map<MessageQueue, ProcessQueue*>::iterator it = m_processQueueTable.begin();
for (; it != m_processQueueTable.end();)
{
MessageQueue mq = it->first;
std::map<std::string, std::set<MessageQueue> >::iterator itm = result.find(mq.getBrokerName());
if (itm == result.end())
{
std::set<MessageQueue> mqs ;
mqs.insert(mq);
result[mq.getBrokerName()] = mqs;
}
else
{
itm->second.insert(mq);
}
}
return result;
}
void RebalanceImpl::rebalanceByTopic(const std::string& topic)
{
RMQ_DEBUG("rebalanceByTopic begin, topic={%s}", topic.c_str());
switch (m_messageModel)
{
case BROADCASTING:
{
//kpr::ScopedLock<kpr::Mutex> lock(m_topicSubscribeInfoTableLock);
std::map<std::string, std::set<MessageQueue> >::iterator it = m_topicSubscribeInfoTable.find(topic);
if (it != m_topicSubscribeInfoTable.end())
{
std::set<MessageQueue> mqSet = it->second;
bool changed = updateProcessQueueTableInRebalance(topic, mqSet);
if (changed)
{
messageQueueChanged(topic, mqSet, mqSet);
RMQ_INFO("messageQueueChanged {%s} {%s} {%s} {%s}",
m_consumerGroup.c_str(),
topic.c_str(),
UtilAll::toString(mqSet).c_str(),
UtilAll::toString(mqSet).c_str());
}
}
else
{
RMQ_WARN("doRebalance, {%s}, but the topic[%s] not exist.", m_consumerGroup.c_str(), topic.c_str());
}
break;
}
case CLUSTERING:
{
//kpr::ScopedLock<kpr::Mutex> lock(m_topicSubscribeInfoTableLock);
std::map<std::string, std::set<MessageQueue> >::iterator it = m_topicSubscribeInfoTable.find(topic);
if (it == m_topicSubscribeInfoTable.end())
{
if (topic.find(MixAll::RETRY_GROUP_TOPIC_PREFIX) != 0)
{
RMQ_WARN("doRebalance, %s, but the topic[%s] not exist.", m_consumerGroup.c_str(), topic.c_str());
}
}
std::list<std::string> cidAll = m_pMQClientFactory->findConsumerIdList(topic, m_consumerGroup);
if (cidAll.empty())
{
RMQ_WARN("doRebalance, %s:%s, get consumer id list failed.", m_consumerGroup.c_str(), topic.c_str());
}
if (it != m_topicSubscribeInfoTable.end() && !cidAll.empty())
{
std::vector<MessageQueue> mqAll;
std::set<MessageQueue> mqSet = it->second;
std::set<MessageQueue>::iterator its = mqSet.begin();
for (; its != mqSet.end(); its++)
{
mqAll.push_back(*its);
}
cidAll.sort();
AllocateMessageQueueStrategy* strategy = m_pAllocateMessageQueueStrategy;
std::vector<MessageQueue>* allocateResult = NULL;
try
{
allocateResult = strategy->allocate(m_consumerGroup,
m_pMQClientFactory->getClientId(), mqAll, cidAll);
}
catch (std::exception& e)
{
RMQ_ERROR("AllocateMessageQueueStrategy.allocate Exception, allocateMessageQueueStrategyName={%s}, mqAll={%s}, cidAll={%s}, %s",
strategy->getName().c_str(), UtilAll::toString(mqAll).c_str(), UtilAll::toString(cidAll).c_str(), e.what());
return;
}
std::set<MessageQueue> allocateResultSet;
if (allocateResult != NULL)
{
for (size_t i = 0; i < allocateResult->size(); i++)
{
allocateResultSet.insert(allocateResult->at(i));
}
delete allocateResult;
}
bool changed = updateProcessQueueTableInRebalance(topic, allocateResultSet);
if (changed)
{
RMQ_INFO("rebalanced result changed. allocateMessageQueueStrategyName={%s}, group={%s}, topic={%s}, ConsumerId={%s}, "
"rebalanceSize={%u}, rebalanceMqSet={%s}, mqAllSize={%u}, cidAllSize={%u}, mqAll={%s}, cidAll={%s}",
strategy->getName().c_str(), m_consumerGroup.c_str(), topic.c_str(), m_pMQClientFactory->getClientId().c_str(),
(unsigned)allocateResultSet.size(), UtilAll::toString(allocateResultSet).c_str(),
(unsigned)mqAll.size(), (unsigned)cidAll.size(), UtilAll::toString(mqAll).c_str(), UtilAll::toString(cidAll).c_str()
);
messageQueueChanged(topic, mqSet, allocateResultSet);
}
}
}
break;
default:
break;
}
RMQ_DEBUG("rebalanceByTopic end");
}
void RebalanceImpl::removeProcessQueue(const MessageQueue& mq)
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::map<MessageQueue, ProcessQueue*>::iterator it = m_processQueueTable.find(mq);
if (it != m_processQueueTable.end())
{
MessageQueue mq = it->first;
ProcessQueue* pq = it->second;
bool isDroped = pq->isDropped();
this->removeUnnecessaryMessageQueue(mq, *pq);
RMQ_INFO("Fix Offset, {%s}, remove unnecessary mq, {%s} Droped: {%d}",
m_consumerGroup.c_str(), mq.toString().c_str(), isDroped);
}
}
bool RebalanceImpl::updateProcessQueueTableInRebalance(const std::string& topic, std::set<MessageQueue>& mqSet)
{
RMQ_DEBUG("updateProcessQueueTableInRebalance begin, topic={%s}", topic.c_str());
bool changed = false;
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::map<MessageQueue, ProcessQueue*>::iterator it = m_processQueueTable.begin();
for (; it != m_processQueueTable.end();)
{
std::map<MessageQueue, ProcessQueue*>::iterator itCur = it++;
MessageQueue mq = itCur->first;
ProcessQueue* pq = itCur->second;
if (mq.getTopic() == topic)
{
std::set<MessageQueue>::iterator itMq = mqSet.find(mq);
if (itMq == mqSet.end())
{
pq->setDropped(true);
if (this->removeUnnecessaryMessageQueue(mq, *pq))
{
changed = true;
m_processQueueTable.erase(itCur);
RMQ_WARN("doRebalance, {%s}, remove unnecessary mq, {%s}",
m_consumerGroup.c_str(), mq.toString().c_str());
}
}
else if (pq->isPullExpired())
{
switch(this->consumeType())
{
case CONSUME_ACTIVELY:
break;
case CONSUME_PASSIVELY:
pq->setDropped(true);
if (this->removeUnnecessaryMessageQueue(mq, *pq))
{
changed = true;
m_processQueueTable.erase(itCur);
RMQ_ERROR("[BUG]doRebalance, {%s}, remove unnecessary mq, {%s}, because pull is pause, so try to fixed it",
m_consumerGroup.c_str(), mq.toString().c_str());
}
break;
default:
break;
}
}
}
}
}
std::list<PullRequest*> pullRequestList;
std::set<MessageQueue>::iterator its = mqSet.begin();
for (; its != mqSet.end(); its++)
{
MessageQueue mq = *its;
bool find = false;
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::map<MessageQueue, ProcessQueue*>::iterator itm = m_processQueueTable.find(mq);
if (itm != m_processQueueTable.end())
{
find = true;
}
}
if (!find)
{
//todo: memleak
PullRequest* pullRequest = new PullRequest();
pullRequest->setConsumerGroup(m_consumerGroup);
pullRequest->setMessageQueue(mq);
pullRequest->setProcessQueue(new ProcessQueue());//todo: memleak
long long nextOffset = computePullFromWhere(mq);
if (nextOffset >= 0)
{
pullRequest->setNextOffset(nextOffset);
pullRequestList.push_back(pullRequest);
changed = true;
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_processQueueTableLock);
m_processQueueTable[mq] = pullRequest->getProcessQueue();
RMQ_INFO("doRebalance, {%s}, add a new mq, {%s}, pullRequst: %s",
m_consumerGroup.c_str(), mq.toString().c_str(), pullRequest->toString().c_str());
}
}
else
{
RMQ_WARN("doRebalance, {%s}, add new mq failed, {%s}",
m_consumerGroup.c_str(), mq.toString().c_str());
}
}
}
//todo memleak
dispatchPullRequest(pullRequestList);
RMQ_DEBUG("updateProcessQueueTableInRebalance end");
return changed;
}
void RebalanceImpl::truncateMessageQueueNotMyTopic()
{
std::map<std::string, SubscriptionData> subTable = getSubscriptionInner();
kpr::ScopedWLock<kpr::RWMutex> lock(m_processQueueTableLock);
std::map<MessageQueue, ProcessQueue*>::iterator it = m_processQueueTable.begin();
for (; it != m_processQueueTable.end();)
{
MessageQueue mq = it->first;
std::map<std::string, SubscriptionData>::iterator itt = subTable.find(mq.getTopic());
if (itt == subTable.end())
{
ProcessQueue* pq = it->second;
if (pq != NULL)
{
pq->setDropped(true);
RMQ_WARN("doRebalance, {%s}, truncateMessageQueueNotMyTopic remove unnecessary mq, {%s}",
m_consumerGroup.c_str(), mq.toString().c_str());
}
m_processQueueTable.erase(it++);
}
else
{
it++;
}
}
}
}