目录
四、最后把查询结果从dbmgr通过网络发送回loginapp
接上一篇loginapp进程接收到登录的请求后会进行数据库查询。
上篇链接:https://blog.csdn.net/hu123he/article/details/119742350
一.loginapp通过网络发送查询消息给dbmgr
void Loginapp::login(Network::Channel* pChannel, MemoryStream& s)
{
......
// 向dbmgr查询用户合法性
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(DbmgrInterface::onAccountLogin);
(*pBundle) << loginName << password;
(*pBundle).appendBlob(datas);
dbmgrinfos->pChannel->send(pBundle);
}
二.dbmgr接收到loginapp发送过来的查询消息
经过网络转发消息最后到达数据库查询服务器dbmgr内
void Dbmgr::onAccountLogin(Network::Channel* pChannel, KBEngine::MemoryStream& s)
{
std::string loginName, password, datas;
s >> loginName >> password;
s.readBlob(datas);
INFO_MSG(fmt::format("Dbmgr::onAccountLogin: loginName={0}.\n",
loginName));
if(loginName.size() == 0)
{
ERROR_MSG("Dbmgr::onAccountLogin: loginName is empty.\n");
return;
}
findBestInterfacesHandler()->loginAccount(pChannel, loginName, password, datas);
}
进入loginAccount内查看
bool InterfacesHandler_Interfaces::loginAccount(Network::Channel* pChannel, std::string& loginName,
std::string& password, std::string& datas)
{
Network::Channel* pInterfacesChannel = Dbmgr::getSingleton().networkInterface().findChannel(addr_);
if (!pInterfacesChannel || pInterfacesChannel->isDestroyed())
{
if (!this->reconnect())
{
return false;
}
pInterfacesChannel = Dbmgr::getSingleton().networkInterface().findChannel(addr_);
}
KBE_ASSERT(pInterfacesChannel);
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(InterfacesInterface::onAccountLogin);
(*pBundle) << pChannel->componentID();
(*pBundle) << loginName << password;
(*pBundle).appendBlob(datas);
pInterfacesChannel->send(pBundle);
return true;
}
可以看到当前还是运行在主线程上的
最后在onLoginAccountCB添加任务,通过线程池处理
void InterfacesHandler_Interfaces::onLoginAccountCB(KBEngine::MemoryStream& s)
{
std::string loginName, accountName, password, postdatas, getdatas;
COMPONENT_ID cid;
SERVER_ERROR_CODE success = SERVER_ERR_OP_FAILED;
s >> cid >> loginName >> accountName >> password >> success;
s.readBlob(postdatas);
s.readBlob(getdatas);
bool needCheckPassword = (success == SERVER_ERR_LOCAL_PROCESSING);
if (success != SERVER_SUCCESS && success != SERVER_ERR_LOCAL_PROCESSING)
accountName = "";
else
success = SERVER_SUCCESS;
Components::ComponentInfos* cinfos = Components::getSingleton().findComponent(LOGINAPP_TYPE, cid);
if(cinfos == NULL || cinfos->pChannel == NULL)
{
ERROR_MSG("InterfacesHandler_Interfaces::onLoginAccountCB: loginapp not found!\n");
return;
}
std::string dbInterfaceName = Dbmgr::getSingleton().selectAccountDBInterfaceName(accountName);
thread::ThreadPool* pThreadPool = DBUtil::pThreadPool(dbInterfaceName);
if (!pThreadPool)
{
ERROR_MSG(fmt::format("InterfacesHandler_Interfaces::onLoginAccountCB: not found dbInterface({})!\n",
dbInterfaceName));
return;
}
pThreadPool->addTask(new DBTaskAccountLogin(cinfos->pChannel->addr(),
loginName, accountName, password, success, postdatas, getdatas, needCheckPassword));
}
三.ThreadPool线程池内进行数据库查询
在pTable->queryAccount(pdbi_, accountName_, info)内组装查询语句
bool DBTaskAccountLogin::db_thread_process()
{
// 如果Interfaces已经判断不成功就没必要继续下去
if(retcode_ != SERVER_SUCCESS)
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): interfaces report failed(errcode={})!\n", retcode_));
return false;
}
retcode_ = SERVER_ERR_OP_FAILED;
if(accountName_.size() == 0)
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): accountName is NULL!\n"));
retcode_ = SERVER_ERR_NAME;
return false;
}
ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
if(pModule == NULL)
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account script[{}], login[{}] failed!\n",
DBUtil::accountScriptName(), accountName_));
retcode_ = SERVER_ERR_SRV_NO_READY;
return false;
}
EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi_->name());
KBEEntityLogTable* pELTable = static_cast<KBEEntityLogTable*>
(entityTables.findKBETable(KBE_TABLE_PERFIX "_entitylog"));
KBE_ASSERT(pELTable);
KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable(KBE_TABLE_PERFIX "_accountinfos"));
KBE_ASSERT(pTable);
ACCOUNT_INFOS info;
info.dbid = 0;
info.flags = 0;
info.deadline = 0;
if(!pTable->queryAccount(pdbi_, accountName_, info))
{
flags_ = info.flags;
deadline_ = info.deadline;
if(ACCOUNT_TYPE(g_kbeSrvConfig.getLoginApp().account_type) != ACCOUNT_TYPE_NORMAL)
{
if (email_isvalid(accountName_.c_str()))
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): account[{}] is email, autocreate failed!\n",
accountName_));
retcode_ = SERVER_ERR_CANNOT_USE_MAIL;
return false;
}
}
if (g_kbeSrvConfig.getDBMgr().notFoundAccountAutoCreate ||
(g_kbeSrvConfig.interfacesAddrs().size() > 0 && !needCheckPassword_/*第三方处理成功则自动创建账号*/))
{
if(!DBTaskCreateAccount::writeAccount(pdbi_, accountName_, password_, postdatas_, info) || info.dbid == 0 || info.flags != ACCOUNT_FLAG_NORMAL)
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): writeAccount[{}] error!\n",
accountName_));
retcode_ = SERVER_ERR_DB;
return false;
}
INFO_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account[{}], autocreate successfully!\n",
accountName_));
info.password = KBE_MD5::getDigest(password_.data(), (int)password_.length());
}
else
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account[{}], login failed!\n",
accountName_));
retcode_ = SERVER_ERR_NOT_FOUND_ACCOUNT;
return false;
}
}
if(info.dbid == 0)
return false;
if(info.flags != ACCOUNT_FLAG_NORMAL)
{
flags_ = info.flags;
return false;
}
if (needCheckPassword_ || g_kbeSrvConfig.interfacesAddrs().size() == 0)
{
if (kbe_stricmp(info.password.c_str(), KBE_MD5::getDigest(password_.data(), (int)password_.length()).c_str()) != 0)
{
retcode_ = SERVER_ERR_PASSWORD;
return false;
}
}
pTable->updateCount(pdbi_, accountName_, info.dbid);
retcode_ = SERVER_ERR_ACCOUNT_IS_ONLINE;
KBEEntityLogTable::EntityLog entitylog;
bool success = !pELTable->queryEntity(pdbi_, info.dbid, entitylog, pModule->getUType());
// 如果有在线纪录
if(!success)
{
componentID_ = entitylog.componentID;
entityID_ = entitylog.entityID;
if(entitylog.serverGroupID != (uint64)getUserUID())
{
serverGroupID_ = entitylog.serverGroupID;
retcode_ = SERVER_ERR_ACCOUNT_LOGIN_ANOTHER_SERVER;
}
}
else
{
retcode_ = SERVER_SUCCESS;
}
dbid_ = info.dbid;
flags_ = info.flags;
deadline_ = info.deadline;
return false;
}
在这里进行查询语句组装
bool KBEAccountTableMysql::queryAccount(DBInterface * pdbi, const std::string& name, ACCOUNT_INFOS& info)
{
std::string sqlstr = "select entityDBID, password, flags, deadline, bindata from " KBE_TABLE_PERFIX "_accountinfos where accountName=\"";
char* tbuf = new char[name.size() * 2 + 1];
mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
tbuf, name.c_str(), name.size());
sqlstr += tbuf;
sqlstr += "\" or email=\"";
sqlstr += tbuf;
sqlstr += "\" LIMIT 1";
SAFE_RELEASE_ARRAY(tbuf);
// 如果查询失败则返回存在, 避免可能产生的错误
if(!pdbi->query(sqlstr.c_str(), sqlstr.size(), false))
return true;
info.dbid = 0;
MYSQL_RES * pResult = mysql_store_result(static_cast<DBInterfaceMysql*>(pdbi)->mysql());
if(pResult)
{
MYSQL_ROW arow = mysql_fetch_row(pResult);
if(arow != NULL)
{
unsigned long *lengths = mysql_fetch_lengths(pResult);
KBEngine::StringConv::str2value(info.dbid, arow[0]);
info.name = name;
info.password = arow[1];
KBEngine::StringConv::str2value(info.flags, arow[2]);
KBEngine::StringConv::str2value(info.deadline, arow[3]);
info.datas.assign(arow[4], lengths[4]);
}
mysql_free_result(pResult);
}
return info.dbid > 0;
}
在工作线程处理完后,pThreadPool->addFiniTask(task)把任务放进已完成容器(finiTaskList_)
#if KBE_PLATFORM == PLATFORM_WIN32
#else
pthread_detach(pthread_self());
#endif
tptd->onStart();
while(isRun)
{
if(tptd->task() != NULL)
{
isRun = true;
}
else
{
tptd->reset_done_tasks();
isRun = tptd->onWaitCondSignal();
}
if(!isRun || pThreadPool->isDestroyed())
{
if(!pThreadPool->hasThread(tptd))
tptd = NULL;
goto __THREAD_END__;
}
TPTask * task = tptd->task();
if(task == NULL)
continue;
tptd->state_ = THREAD_STATE_BUSY;
while(task && !tptd->threadPool()->isDestroyed())
{
tptd->inc_done_tasks();
tptd->onProcessTaskStart(task);
tptd->processTask(task);
tptd->onProcessTaskEnd(task);
// 尝试继续从任务队列里取出一个繁忙的未处理的任务
TPTask * task1 = tptd->tryGetTask();
if(!task1)
{
tptd->state_ = THREAD_STATE_PENDING;
tptd->onTaskCompleted();
break;
}
else
{
pThreadPool->addFiniTask(task);
task = task1;
tptd->task(task1);
}
}
}
__THREAD_END__:
if(tptd)
{
TPTask * task = tptd->task();
if(task)
{
WARNING_MSG(fmt::format("TPThread::threadFunc: task {0:p} not finish, thread.{1:p} will exit.\n",
(void*)task, (void*)tptd));
delete task;
}
tptd->onEnd();
tptd->state_ = THREAD_STATE_END;
tptd->reset_done_tasks();
}
#if KBE_PLATFORM == PLATFORM_WIN32
return 0;
#else
pthread_exit(NULL);
return NULL;
#endif
}
四、最后把查询结果从dbmgr通过网络发送回loginapp
主线程会遍历完成容器(finiTaskList_)把返回消息通过网络发送回申请查询的服务器。
thread::TPTask::TPTaskState DBTaskAccountLogin::presentMainThread()
{
DEBUG_MSG(fmt::format("Dbmgr::onAccountLogin:loginName={}, accountName={}, success={}, componentID={}, entityID={}, dbid={}, flags={}, deadline={}.\n",
loginName_,
accountName_,
retcode_,
componentID_,
entityID_,
dbid_,
flags_,
deadline_
));
if(serverGroupID_ > 0)
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::presentMainThread: entitylog serverGroupID not match. loginName={}, accountName={}, self={}\n",
loginName_, accountName_, serverGroupID_, (uint64)getUserUID()));
componentID_ = 0;
entityID_ = 0;
}
// 一个用户登录, 构造一个数据库查询指令并加入到执行队列, 执行完毕将结果返回给loginapp
Network::Bundle* pBundle = Network::Bundle::createPoolObject(OBJECTPOOL_POINT);
(*pBundle).newMessage(LoginappInterface::onLoginAccountQueryResultFromDbmgr);
(*pBundle) << retcode_;
(*pBundle) << loginName_;
(*pBundle) << accountName_;
(*pBundle) << password_;
(*pBundle) << needCheckPassword_;
(*pBundle) << componentID_; // 如果大于0则表示账号还存活在某个baseapp上
(*pBundle) << entityID_;
(*pBundle) << dbid_;
(*pBundle) << flags_;
(*pBundle) << deadline_;
(*pBundle).appendBlob(getdatas_);
if(!this->send(pBundle))
{
ERROR_MSG(fmt::format("DBTaskAccountLogin::presentMainThread: channel({}) not found.\n", addr_.c_str()));
Network::Bundle::reclaimPoolObject(pBundle);
}
return DBTask::presentMainThread();
}
总结
可以看见dbmgr内消息收发都是在主线程, 线程池主要是负责进行数据库查询。