blob: 03b8ca7b54bc809b4bca1625ed55b145f170e945 [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 "TcpRemotingClient.h"
#include "MQClientException.h"
#include "TcpRequestProcessor.h"
#include "MQProtos.h"
#include "ThreadPoolWork.h"
namespace rmq
{
ProcessDataWork::ProcessDataWork(TcpRemotingClient* pClient, TcpTransport* pTts, std::string* pData)
: m_pClient(pClient), m_pTts(pTts), m_pData(pData)
{
}
ProcessDataWork::~ProcessDataWork()
{
delete m_pData;
}
void ProcessDataWork::Do()
{
try
{
m_pClient->processData(m_pTts, m_pData);
}
catch (std::exception& e)
{
RMQ_ERROR("processDataWork catch Exception: %s", e.what());
}
catch (...)
{
RMQ_ERROR("processDataWork catch Exception");
}
}
TcpRemotingClient::TcpRemotingClient(const RemoteClientConfig& config)
: m_stop(false), m_epoller(false), m_config(config),
m_semaphoreOneway(s_ClientOnewaySemaphoreValue), m_semaphoreAsync(s_ClientAsyncSemaphoreValue)
{
m_pNetThreadPool = new kpr::ThreadPool("NetClientThreadPool", 5, 5, 20);
m_pEventThread = new EventThread(*this);
SocketInit();
m_epoller.create(10240);
}
TcpRemotingClient::~TcpRemotingClient()
{
SocketUninit();
}
void TcpRemotingClient::start()
{
RMQ_DEBUG("TcpRemotingClient::start()");
m_pEventThread->Start();
}
void TcpRemotingClient::shutdown()
{
RMQ_DEBUG("TcpRemotingClient::shutdown()");
m_stop = true;
m_pNetThreadPool->Destroy();
m_pEventThread->Join();
}
/*
void printMsg(const std::string& prefix, const char* pData, int len)
{
int headLen;
memcpy(&headLen, pData + 4, 4);
headLen = ntohl(headLen);
RMQ_DEBUG("%s|decode[%d,%d,%d]|%s%s", prefix.c_str(), len, headLen, len - 8 - headLen, std::string(pData + 8, headLen).c_str(),
std::string(pData + 8 + headLen, len - 8 - headLen).c_str());
}
*/
void TcpRemotingClient::run()
{
RMQ_INFO("EventThread run begin: %lld", KPRUtil::GetCurrentTimeMillis());
do
{
try
{
int nfds = m_epoller.wait(500);
if (nfds > 0)
{
int ret = 0;
std::vector<TcpTransport*> errTts;
for (int i = 0; i < nfds && !m_stop; ++i)
{
const epoll_event& ev = m_epoller.get(i);
std::map<std::string , TcpTransport*>::iterator it;
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_transportTableLock);
it = m_transportTable.find((char*)ev.data.ptr);
if (it == m_transportTable.end())
{
continue;
}
}
TcpTransport* pTts = it->second;
if (ev.events & EPOLLERR || ev.events & EPOLLHUP)
{
RMQ_ERROR("recvData fail, err=%d(%s), pts=%p", errno, strerror(errno), pTts);
errTts.push_back(pTts);
}
if (ev.events & EPOLLIN)
{
std::list<std::string*> dataList;
ret = pTts->recvData(dataList);
if (ret < 0)
{
RMQ_ERROR("recvData fail, ret=%d, errno=%d, pts=%p", ret, NET_ERROR, pTts);
errTts.push_back(pTts);
}
if (dataList.size() > 0)
{
for (typeof(dataList.begin()) it = dataList.begin();
it != dataList.end(); it++)
{
//printMsg("run", (*it)->c_str(), (*it)->size());
kpr::ThreadPoolWorkPtr work = new ProcessDataWork(this, pTts, *it);
m_pNetThreadPool->AddWork(work);
}
}
}
}
std::vector<TcpTransport*>::iterator itErr = errTts.begin();
for (; itErr != errTts.end(); itErr++)
{
removeTTS(*itErr, true);
}
}
handleTimerEvent();
}
catch (...)
{
RMQ_ERROR("TcpRemotingClient.run catch exception");
}
}
while (!m_stop);
handleTimerEvent();
RMQ_INFO("EventThread run end: %lld", KPRUtil::GetCurrentTimeMillis());
}
void TcpRemotingClient::updateNameServerAddressList(const std::vector<std::string>& addrs)
{
m_namesrvAddrList = addrs;
m_namesrvIndex = 0;
}
std::vector<std::string> TcpRemotingClient::getNameServerAddressList()
{
return m_namesrvAddrList;
}
void TcpRemotingClient::registerProcessor(int requestCode, TcpRequestProcessor* pProcessor)
{
m_processorTable[requestCode] = pProcessor;
}
RemotingCommand* TcpRemotingClient::invokeSync(const std::string& addr,
RemotingCommand* pRequest,
int timeoutMillis)
{
TcpTransport* pTts = getAndCreateTransport(addr, timeoutMillis);
if (pTts != NULL && pTts->isConnected())
{
RemotingCommand* pResponse = NULL;
try
{
pResponse = invokeSyncImpl(pTts, pRequest, timeoutMillis);
}
catch(const RemotingSendRequestException& e)
{
RMQ_WARN("invokeSync: send pRequest exception, so close the channel[{%s}]",
pTts->getServerAddr().c_str());
removeTTS(pTts, false);
throw e;
}
catch(const RemotingTimeoutException& e)
{
RMQ_WARN("invokeSync: wait response timeout exception, the channel[{%s}], timeout=%d",
pTts->getServerAddr().c_str(), timeoutMillis);
throw e;
}
return pResponse;
}
else
{
removeTTS(pTts, false);
THROW_MQEXCEPTION(RemotingConnectException, "connect fail", -1);
//return NULL;
}
}
void TcpRemotingClient::invokeAsync(const std::string& addr,
RemotingCommand* pRequest,
int timeoutMillis,
InvokeCallback* pInvokeCallback)
{
TcpTransport* pTts = getAndCreateTransport(addr, timeoutMillis);
if (pTts != NULL && pTts->isConnected())
{
try
{
this->invokeAsyncImpl(pTts, pRequest, timeoutMillis, pInvokeCallback);
}
catch (const RemotingSendRequestException& e)
{
RMQ_WARN("invokeAsync: send pRequest exception, so close the channel[{%s}]", addr.c_str());
removeTTS(pTts, false);
throw e;
}
return;
}
else
{
removeTTS(pTts, false);
std::string msg;msg.append("connect to <").append(addr).append("> failed");
THROW_MQEXCEPTION(RemotingConnectException, msg, -1);
}
}
int TcpRemotingClient::invokeOneway(const std::string& addr,
RemotingCommand* pRequest,
int timeoutMillis)
{
TcpTransport* pTts = getAndCreateTransport(addr, timeoutMillis);
if (pTts != NULL && pTts->isConnected())
{
return invokeOnewayImpl(pTts, pRequest, timeoutMillis);
}
else
{
removeTTS(pTts, false);
return -1;
}
}
TcpTransport* TcpRemotingClient::getAndCreateTransport(const std::string& addr, int timeoutMillis)
{
if (addr.empty())
{
return getAndCreateNameserverTransport(timeoutMillis);
}
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_transportTableLock);
std::map<std::string , TcpTransport*>::iterator it = m_transportTable.find(addr);
if (it != m_transportTable.end())
{
return it->second;
}
}
return this->createTransport(addr, timeoutMillis);
}
TcpTransport* TcpRemotingClient::createTransport(const std::string& addr, int timeoutMillis)
{
TcpTransport* pTts = NULL;
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_transportTableLock);
std::map<std::string , TcpTransport*>::iterator it = m_transportTable.find(addr);
if (it != m_transportTable.end())
{
return it->second;
}
}
if (m_transportTableLock.TryWriteLock(s_LockTimeoutMillis))
{
std::map<std::string , TcpTransport*>::iterator it = m_transportTable.find(addr);
if (it != m_transportTable.end())
{
return it->second;
}
std::map<std::string , std::string> config;
pTts = new TcpTransport(config);
if (pTts->connect(addr, timeoutMillis) != CLIENT_ERROR_SUCCESS)
{
delete pTts;
pTts = NULL;
RMQ_INFO("[NETWORK]: CONNECT {%s} failed", addr.c_str());
}
else
{
m_transportTable[addr] = pTts;
m_epoller.add(pTts->getSocket(), (long long)((pTts->getServerAddr()).c_str()), EPOLLIN);
RMQ_INFO("[NETWORK]: CONNECT => {%s} success", addr.c_str());
}
m_transportTableLock.Unlock();
}
else
{
RMQ_WARN("createTransport: try to lock m_transportTable, but timeout, {%d}ms", timeoutMillis);
}
return pTts;
}
TcpTransport* TcpRemotingClient::getAndCreateNameserverTransport(int timeoutMillis)
{
TcpTransport* pTts = NULL;
if (m_namesrvAddrChoosed.get() != NULL)
{
std::string addr = *m_namesrvAddrChoosed;
if (!addr.empty())
{
pTts = getAndCreateTransport(addr, timeoutMillis);
if (pTts != NULL)
{
return pTts;
}
}
}
if (m_namesrvAddrChoosedLock.TryLock(s_LockTimeoutMillis))
{
if (m_namesrvAddrChoosed.get() != NULL)
{
std::string addr = *m_namesrvAddrChoosed;
if (!addr.empty())
{
pTts = getAndCreateTransport(addr, timeoutMillis);
if (pTts != NULL)
{
m_namesrvAddrChoosedLock.Unlock();
return pTts;
}
}
}
if (!m_namesrvAddrList.empty())
{
for (size_t i = 0; i < m_namesrvAddrList.size(); i++)
{
int index = abs(++m_namesrvIndex) % m_namesrvAddrList.size();
std::string& newAddr = m_namesrvAddrList.at(index);
m_namesrvAddrChoosed.set(&newAddr);
TcpTransport* pTts = getAndCreateTransport(newAddr, timeoutMillis);
if (pTts != NULL)
{
m_namesrvAddrChoosedLock.Unlock();
return pTts;
}
}
}
m_namesrvAddrChoosedLock.Unlock();
}
return NULL;
}
void TcpRemotingClient::handleTimerEvent()
{
// every 1000ms
static unsigned long long lastTime = 0;
if (!m_stop && (int)(KPRUtil::GetCurrentTimeMillis() - lastTime) < s_CheckIntervalMillis)
{
return;
}
try
{
lastTime = KPRUtil::GetCurrentTimeMillis();
this->scanResponseTable();
this->scanCloseTransportTable();
}
catch(...)
{
RMQ_ERROR("scanResponseTable exception");
}
}
void TcpRemotingClient::scanCloseTransportTable()
{
if (m_closeTransportTable.empty())
{
return;
}
if (m_closeTransportTableLock.TryLock())
{
std::list<TcpTransport*>::iterator it;
for( it = m_closeTransportTable.begin(); it != m_closeTransportTable.end(); )
{
TcpTransport* pTts = *it;
long long diffTime = KPRUtil::GetCurrentTimeMillis() - pTts->getLastSendRecvTime();
if (m_stop || (diffTime > 5000))
{
RMQ_WARN("remove close connection, %lld, {%s}", diffTime, pTts->getServerAddr().c_str());
it = m_closeTransportTable.erase(it);
delete pTts;
}
else
{
it++;
}
}
m_closeTransportTableLock.Unlock();
}
else
{
RMQ_WARN("m_closeTransportTableLock TryLock fail");
}
}
void TcpRemotingClient::scanResponseTable()
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_responseTableLock);
for(typeof(m_responseTable.begin()) it = m_responseTable.begin();it != m_responseTable.end();)
{
long long diffTime = KPRUtil::GetCurrentTimeMillis() - it->second->getBeginTimestamp();
if (m_stop || (diffTime > it->second->getTimeoutMillis() + 2000))
{
RMQ_WARN("remove timeout request, %lld, %s", diffTime, it->second->toString().c_str());
try
{
it->second->executeInvokeCallback();
}
catch(...)
{
RMQ_WARN("scanResponseTable, operationComplete Exception");
}
it->second->release();
m_responseTable.erase(it++);
}
else
{
it++;
}
}
}
void TcpRemotingClient::processData(TcpTransport* pTts, std::string* pData)
{
//printMsg("processData", pData->c_str(), pData->size());
RemotingCommand* pCmd = RemotingCommand::decode(pData->data(), (int)pData->size());
if (pCmd == NULL)
{
RMQ_ERROR("invalid data format, len:%d, data: %s", (int)pData->size(), pData->c_str());
return;
}
int code = 0;
if (pCmd->isResponseType())
{
kpr::ScopedRLock<kpr::RWMutex> lock(m_responseTableLock);
std::map<int, ResponseFuturePtr>::iterator it = m_responseTable.find(pCmd->getOpaque());
if (it != m_responseTable.end())
{
code = it->second->getRequestCode();
}
else
{
RMQ_WARN("receive response, but not matched any request, maybe timeout or oneway, pCmd: %s", pCmd->toString().c_str());
delete pCmd;
return;
}
}
else
{
code = pCmd->getCode();
}
pCmd->makeCustomHeader(code, pData->data(), (int)pData->size());
if (pCmd->isResponseType())
{
RMQ_DEBUG("[NETWORK]: RECV => {%s}, {opaque=%d, requst.code=%s(%d), response.code=%s(%d)}, %s",
pTts->getServerAddr().c_str(), pCmd->getOpaque(), getMQRequestCodeString(code), code,
getMQResponseCodeString(pCmd->getCode()), pCmd->getCode(), pCmd->toString().c_str());
}
else
{
RMQ_DEBUG("[NETWORK]: RECV => {%s}, {opaque=%d, requst.code=%s(%d)}, %s",
pTts->getServerAddr().c_str(), pCmd->getOpaque(),
getMQRequestCodeString(code), code, pCmd->toString().c_str());
}
processMessageReceived(pTts, pCmd);
}
RemotingCommand* TcpRemotingClient::invokeSyncImpl(TcpTransport* pTts,
RemotingCommand* pRequest,
int timeoutMillis)
{
ResponseFuturePtr pResponseFuture = new ResponseFuture(
pRequest->getCode(), pRequest->getOpaque(), timeoutMillis,
NULL, true, NULL);
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_responseTableLock);
m_responseTable.insert(std::pair<int, ResponseFuturePtr>(pRequest->getOpaque(), pResponseFuture));
}
int ret = sendCmd(pTts, pRequest, timeoutMillis);
if (ret == 0)
{
pResponseFuture->setSendRequestOK(true);
}
else
{
pResponseFuture->setSendRequestOK(false);
pResponseFuture->putResponse(NULL);
RMQ_WARN("send a pRequest command to channel <%s> failed.", pTts->getServerAddr().c_str());
}
RemotingCommand* pResponse = pResponseFuture->waitResponse(timeoutMillis);
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_responseTableLock);
std::map<int, ResponseFuturePtr>::iterator it = m_responseTable.find(pRequest->getOpaque());
if (it != m_responseTable.end())
{
m_responseTable.erase(it);
}
}
if (pResponse == NULL)
{
if (ret == 0)
{
std::stringstream oss;
oss << "wait response on the channel <" << pTts->getServerAddr() << "> timeout," << timeoutMillis << "ms";
THROW_MQEXCEPTION(RemotingTimeoutException, oss.str(), -1);
}
else
{
std::stringstream oss;
oss << "send request to <" << pTts->getServerAddr() << "> failed";
THROW_MQEXCEPTION(RemotingSendRequestException, oss.str(), -1);
}
}
return pResponse;
}
void TcpRemotingClient::invokeAsyncImpl(TcpTransport* pTts,
RemotingCommand* pRequest,
int timeoutMillis,
InvokeCallback* pInvokeCallback)
{
bool acquired = m_semaphoreAsync.Wait(timeoutMillis);
if (acquired)
{
ResponseFuturePtr pResponseFuture = new ResponseFuture(
pRequest->getCode(), pRequest->getOpaque(), timeoutMillis,
pInvokeCallback, false, &m_semaphoreAsync);
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_responseTableLock);
m_responseTable.insert(std::pair<int, ResponseFuturePtr>(pRequest->getOpaque(), pResponseFuture));
}
int ret = sendCmd(pTts, pRequest, timeoutMillis);
if (ret == 0)
{
pResponseFuture->setSendRequestOK(true);
}
else
{
pResponseFuture->setSendRequestOK(false);
pResponseFuture->putResponse(NULL);
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_responseTableLock);
std::map<int, ResponseFuturePtr>::iterator it = m_responseTable.find(pRequest->getOpaque());
if (it != m_responseTable.end())
{
m_responseTable.erase(it);
}
}
try
{
pResponseFuture->executeInvokeCallback();
}
catch (...)
{
RMQ_WARN("executeInvokeCallback exception");
}
pResponseFuture->release();
RMQ_WARN("send a pRequest command to channel <%s> failed, requet: %s",
pTts->getServerAddr().c_str(), pRequest->toString().c_str());
}
}
else
{
if (timeoutMillis <= 0)
{
THROW_MQEXCEPTION(RemotingTooMuchRequestException, "invokeAsyncImpl invoke too fast", -1);
}
else
{
std::string info = RocketMQUtil::str2fmt(
"invokeAsyncImpl wait semaphore timeout, %dms, semaphoreAsyncValue: %d, request: %s",
timeoutMillis,
m_semaphoreAsync.GetValue(),
pRequest->toString().c_str()
);
RMQ_WARN("%s", info.c_str());
THROW_MQEXCEPTION(RemotingTimeoutException, info, -1);
}
}
return;
}
int TcpRemotingClient::invokeOnewayImpl(TcpTransport* pTts,
RemotingCommand* pRequest,
int timeoutMillis)
{
pRequest->markOnewayRPC();
bool acquired = m_semaphoreOneway.Wait(timeoutMillis);
if (acquired)
{
int ret = sendCmd(pTts, pRequest, timeoutMillis);
m_semaphoreOneway.Release();
if (ret != 0)
{
RMQ_WARN("send a pRequest command to channel <%s> failed, requet: %s",
pTts->getServerAddr().c_str(), pRequest->toString().c_str());
THROW_MQEXCEPTION(RemotingSendRequestException, std::string("send request to <") + pTts->getServerAddr() + "> fail", -1);
}
}
else
{
if (timeoutMillis <= 0)
{
THROW_MQEXCEPTION(RemotingTooMuchRequestException, "invokeOnewayImpl invoke too fast", -1);
}
else
{
std::string info = RocketMQUtil::str2fmt(
"invokeOnewayImpl wait semaphore timeout, %dms, semaphoreAsyncValue: %d, request: %s",
timeoutMillis,
m_semaphoreAsync.GetValue(),
pRequest->toString().c_str()
);
RMQ_WARN("%s", info.c_str());
THROW_MQEXCEPTION(RemotingTimeoutException, info, -1);
}
}
return 0;
}
void TcpRemotingClient::processMessageReceived(TcpTransport* pTts, RemotingCommand* pCmd)
{
try
{
switch (pCmd->getType())
{
case REQUEST_COMMAND:
processRequestCommand(pTts, pCmd);
break;
case RESPONSE_COMMAND:
processResponseCommand(pTts, pCmd);
break;
default:
break;
}
}
catch (std::exception& e)
{
RMQ_ERROR("processMessageReceived catch Exception: %s", e.what());
}
catch (...)
{
RMQ_ERROR("processMessageReceived catch Exception");
}
}
void TcpRemotingClient::processRequestCommand(TcpTransport* pTts, RemotingCommand* pCmd)
{
RMQ_DEBUG("receive request from server, cmd: %s", pCmd->toString().c_str());
RemotingCommandPtr pResponse = NULL;
std::map<int, TcpRequestProcessor*>::iterator it = m_processorTable.find(pCmd->getCode());
if (it != m_processorTable.end())
{
try
{
pResponse = it->second->processRequest(pTts, pCmd);
if (!pCmd->isOnewayRPC())
{
if (pResponse.ptr() != NULL)
{
pResponse->setOpaque(pCmd->getOpaque());
pResponse->markResponseType();
int ret = this->sendCmd(pTts, pResponse, 3000);
if (ret != 0)
{
RMQ_ERROR("process request over, but response failed");
}
}
else
{
// ignore
}
}
}
catch (const std::exception& e)
{
RMQ_ERROR("process request exception:%s", e.what());
if (!pCmd->isOnewayRPC())
{
pResponse = RemotingCommand::createResponseCommand(
SYSTEM_ERROR_VALUE, e.what(), NULL);
pResponse->setOpaque(pCmd->getOpaque());
int ret = this->sendCmd(pTts, pResponse, 3000);
if (ret != 0)
{
RMQ_ERROR("process request over, but response failed");
}
}
}
}
else
{
pResponse = RemotingCommand::createResponseCommand(
REQUEST_CODE_NOT_SUPPORTED_VALUE, "request type not supported", NULL);
pResponse->setOpaque(pCmd->getOpaque());
int ret = this->sendCmd(pTts, pResponse, 3000);
if (ret != 0)
{
RMQ_ERROR("process request over, but pResponse failed");
}
}
delete pCmd;
}
void TcpRemotingClient::processResponseCommand(TcpTransport* pTts, RemotingCommand* pCmd)
{
ResponseFuturePtr res = NULL;
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_responseTableLock);
std::map<int, ResponseFuturePtr>::iterator it = m_responseTable.find(pCmd->getOpaque());
if (it != m_responseTable.end())
{
res = it->second;
res->release();
m_responseTable.erase(it);
}
}
if (res)
{
res->putResponse(pCmd);
res->executeInvokeCallback();
}
else
{
RMQ_WARN("receive response, but not matched any request, cmd: %s", pCmd->toString().c_str());
delete pCmd;
}
}
int TcpRemotingClient::sendCmd(TcpTransport* pTts, RemotingCommand* pRequest, int timeoutMillis)
{
pRequest->encode();
int ret = pTts->sendData(pRequest->getData(), pRequest->getDataLen(), timeoutMillis);
RMQ_DEBUG("[NETWORK]: SEND => {%s}, {opaque=%d, request.code=%s(%d), ret=%d, timeout=%d}, %s",
pTts->getServerAddr().c_str(), pRequest->getOpaque(),
getMQRequestCodeString(pRequest->getCode()), pRequest->getCode(),
ret, timeoutMillis, pRequest->toString().c_str());
return ret;
}
void TcpRemotingClient::removeTTS(TcpTransport* pTts, bool isDisConnected)
{
if (pTts)
{
RMQ_INFO("[NETWORK]: %s => {%s}", isDisConnected ? "DISCONNECT" : "CLOSE",
pTts->getServerAddr().c_str());
bool bNeedClear = false;
m_epoller.del(pTts->getSocket(), (long long)(pTts->getServerAddr().c_str()), 0);
{
kpr::ScopedWLock<kpr::RWMutex> lock(m_transportTableLock);
std::map<std::string , TcpTransport*>::iterator it = m_transportTable.find(pTts->getServerAddr());
if (it != m_transportTable.end())
{
if (it->second == pTts)
{
m_transportTable.erase(it);
bNeedClear = true;
}
}
}
if (bNeedClear)
{
kpr::ScopedLock<kpr::Mutex> lock(m_closeTransportTableLock);
m_closeTransportTable.push_back(pTts);
}
}
}
}