KBEngine,数据库查询消息流程

目录

一.loginapp通过网络发送查询消息给dbmgr

二.dbmgr接收到loginapp发送过来的查询消息

三.ThreadPool线程池内进行数据库查询

四、最后把查询结果从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内消息收发都是在主线程, 线程池主要是负责进行数据库查询。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值