KBEngine warring项目源码阅读(二) 登录和baseapp的负载均衡

本文详细解析了KBEngine项目的登录过程,包括客户端登录请求、服务器数据库查询、BaseApp负载均衡策略的实现。在负载均衡中,首先确保BaseApp进程存活并初始化,然后选择负载最小的进程分配登录请求。服务器BaseApp接收到消息后,创建或查询实体,并通过mailbox进行后续通信。最后在客户端完成mailbox绑定,启动角色列表请求。
摘要由CSDN通过智能技术生成

原本不打算把登录拿出来写的,但是阅读登录部分的代码的时候发现登录和注册还不太一回事,因为登录涉及到分配baseapp的ip,负载均衡的实现,所以水一下。

流程图:

和上次一样,先是找unity控件

找到ui.cs下的login

复制代码

 1     void login()
 2     {
 3         Common.DEBUG_MSG("login is Click, name=" + username.input.text + ", password=" + password.input.text + "!");
 4         
 5         log_label.obj.text = "请求连接服务器...";
 6         log_label.obj.color = UnityEngine.Color.green;
 7         if(username.input.text == "" || username.input.text.Length > 30)
 8         {
 9             log_label.obj.text = "用户名或者邮箱地址不合法。";
10             log_label.obj.color = UnityEngine.Color.red;
11             Common.WARNING_MSG("ui::login: invalid username!");
12             return;
13         }
14         
15         if(password.input.text.Length < 6 || password.input.text.Length > 16)
16         {
17             log_label.obj.text = "密码不合法, 长度应该在6-16位之间。";
18             log_label.obj.color = UnityEngine.Color.red;
19             Common.WARNING_MSG("ui::login: invalid reg_password!");
20             return;
21         }
22         
23         KBEngine.Event.fireIn("login", username.input.text, password.input.text, System.Text.Encoding.UTF8.GetBytes("kbengine_unity_warring"));
24         log_label.obj.text = "连接成功,等待处理请稍后...";
25     }

复制代码

根据上篇文章思路找到Kbengine.cs下的login_loginapp

复制代码

        /*
            登录到服务端(loginapp), 登录成功后还必须登录到网关(baseapp)登录流程才算完毕
        */
        public void login_loginapp(bool noconnect)
        {
            if(noconnect)
            {
                reset();
                _networkInterface.connectTo(_args.ip, _args.port, onConnectTo_loginapp_callback, null);
            }
            else
            {
                Dbg.DEBUG_MSG("KBEngine::login_loginapp(): send login! username=" + username);
                Bundle bundle = Bundle.createObject();
                bundle.newMessage(Message.messages["Loginapp_login"]);
                bundle.writeInt8((sbyte)_args.clientType);
                bundle.writeBlob(KBEngineApp.app._clientdatas);
                bundle.writeString(username);
                bundle.writeString(password);
                bundle.send(_networkInterface);
            }
        }

复制代码

去服务器的loginapp项目找login

复制代码

//-------------------------------------------------------------------------------------
void Loginapp::login(Network::Channel* pChannel, MemoryStream& s)
{
    AUTO_SCOPED_PROFILE("login");

    COMPONENT_CLIENT_TYPE ctype;
    CLIENT_CTYPE tctype = UNKNOWN_CLIENT_COMPONENT_TYPE;
    std::string loginName;
    std::string password;
    std::string datas;

    // 前端类别
    s >> tctype;
    ctype = static_cast<COMPONENT_CLIENT_TYPE>(tctype);
    
    // 附带数据
    s.readBlob(datas);

    // 帐号登录名
    s >> loginName;

    // 密码
    s >> password;

    loginName = KBEngine::strutil::kbe_trim(loginName);
    if(loginName.size() == 0)
    {
        INFO_MSG("Loginapp::login: loginName is NULL.\n");
        _loginFailed(pChannel, loginName, SERVER_ERR_NAME, datas, true);
        s.done();
        return;
    }

    if(loginName.size() > ACCOUNT_NAME_MAX_LENGTH)
    {
        INFO_MSG(fmt::format("Loginapp::login: loginName is too long, size={}, limit={}.\n",
            loginName.size(), ACCOUNT_NAME_MAX_LENGTH));
        
        _loginFailed(pChannel, loginName, SERVER_ERR_NAME, datas, true);
        s.done();
        return;
    }

    if(password.size() > ACCOUNT_PASSWD_MAX_LENGTH)
    {
        INFO_MSG(fmt::format("Loginapp::login: password is too long, size={}, limit={}.\n",
            password.size(), ACCOUNT_PASSWD_MAX_LENGTH));
        
        _loginFailed(pChannel, loginName, SERVER_ERR_PASSWORD, datas, true);
        s.done();
        return;
    }
    
    if(datas.size() > ACCOUNT_DATA_MAX_LENGTH)
    {
        INFO_MSG(fmt::format("Loginapp::login: bindatas is too long, size={}, limit={}.\n",
            datas.size(), ACCOUNT_DATA_MAX_LENGTH));
        
        _loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
        s.done();
        return;
    }

    // 首先必须baseappmgr和dbmgr都已经准备完毕了。
    Components::ComponentInfos* baseappmgrinfos = Components::getSingleton().getBaseappmgr();
    if(baseappmgrinfos == NULL || baseappmgrinfos->pChannel == NULL || baseappmgrinfos->cid == 0)
    {
        datas = "";
        _loginFailed(pChannel, loginName, SERVER_ERR_SRV_NO_READY, datas, true);
        s.done();
        return;
    }

    Components::ComponentInfos* dbmgrinfos = Components::getSingleton().getDbmgr();
    if(dbmgrinfos == NULL || dbmgrinfos->pChannel == NULL || dbmgrinfos->cid == 0)
    {
        datas = "";
        _loginFailed(pChannel, loginName, SERVER_ERR_SRV_NO_READY, datas, true);
        s.done();
        return;
    }

    if(!g_kbeSrvConfig.getDBMgr().allowEmptyDigest)
    {
        std::string clientDigest;

        if(s.length() > 0)
            s >> clientDigest;

        if(clientDigest.size() > 0)
        {
            if(clientDigest != digest_)
            {
                INFO_MSG(fmt::format("Loginapp::login: loginName({}), digest not match. curr({}) != dbmgr({})\n",
                    loginName, clientDigest, digest_));

                datas = "";
                _loginFailed(pChannel, loginName, SERVER_ERR_ENTITYDEFS_NOT_MATCH, datas, true);
                return;
            }
        }
        else
        {
            //WARNING_MSG(fmt::format("Loginapp::login: loginName={} no check entitydefs!\n", loginName));
        }
    }

    s.done();

    if(shuttingdown_ != SHUTDOWN_STATE_STOP)
    {
        INFO_MSG(fmt::format("Loginapp::login: shutting down, {} login failed!\n", loginName));

        datas = "";
        _loginFailed(pChannel, loginName, SERVER_ERR_IN_SHUTTINGDOWN, datas, true);
        return;
    }

    if(initProgress_ < 1.f)
    {
        datas = fmt::format("initProgress: {}", initProgress_);
        _loginFailed(pChannel, loginName, SERVER_ERR_SRV_STARTING, datas, true);
        return;
    }
    
    // 把请求交由脚本处理
    SCOPED_PROFILE(SCRIPTCALL_PROFILE);
    PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(), 
                                        const_cast<char*>("onReuqestLogin"), 
                                        const_cast<char*>("ssby#"), 
                                        loginName.c_str(),
                                        password.c_str(),
                                        tctype,
                                        datas.c_str(), datas.length());

    if(pyResult != NULL)
    {
        bool login_check = true;
        if(PySequence_Check(pyResult) && PySequence_Size(pyResult) == 5)
        {
            char* sname;
            char* spassword;
            char *extraDatas;
            Py_ssize_t extraDatas_size = 0;
            SERVER_ERROR_CODE error;
            
            if(PyArg_ParseTuple(pyResult, "H|s|s|b|y#",  &error, &sname, &spassword, &tctype, &extraDatas, &extraDatas_size) == -1)
            {
                ERROR_MSG(fmt::format("Loginapp::login: {}.onReuqestLogin, Return value error! loginName={}\n", 
                    g_kbeSrvConfig.getLoginApp().entryScriptFile, loginName));

                login_check = false;
                _loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
            }
            
            if(login_check)
            {
                loginName = sname;
                password = spassword;

                if (extraDatas && extraDatas_size > 0)
                    datas.assign(extraDatas, extraDatas_size);
                else
                    SCRIPT_ERROR_CHECK();
            }
            
            if(error != SERVER_SUCCESS)
            {
                login_check = false;
                _loginFailed(pChannel, loginName, error, datas, true);
            }
            
            if(loginName.size() == 0)
            {
                INFO_MSG("Loginapp::login: loginName is NULL.\n");
                _loginFailed(pChannel, loginName, SERVER_ERR_NAME, datas, true);
                s.done();
                return;
            }
        }
        else
        {
            ERROR_MSG(fmt::format("Loginapp::login: {}.onReuqestLogin, Return value error, must be errorcode or tuple! loginName={}\n", 
                g_kbeSrvConfig.getLoginApp().entryScriptFile, loginName));

            login_check = false;
            _loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
        }
        
        Py_DECREF(pyResult);
        
        if(!login_check)
            return;
    }
    else
    {
        SCRIPT_ERROR_CHECK();
        _loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
    }

    PendingLoginMgr::PLInfos* ptinfos = pendingLoginMgr_.find(loginName);
    if(ptinfos != NULL)
    {
        datas = "";
        _loginFailed(pChannel, loginName, SERVER_ERR_BUSY, datas, true);
        return;
    }

    ptinfos = new PendingLoginMgr::PLInfos;
    ptinfos->ctype = ctype;
    ptinfos->datas = datas;
    ptinfos->accountName = loginName;
    ptinfos->password = password;
    ptinfos->addr = pChannel->addr();
    pendingLoginMgr_.add(ptinfos);

    if(ctype < UNKNOWN_CLIENT_COMPONENT_TYPE || ctype >= CLIENT_TYPE_END)
        ctype = UNKNOWN_CLIENT_COMPONENT_TYPE;

    INFO_MSG(fmt::format("Loginapp::login: new client[{0}], loginName={1}, datas={2}.\n",
        COMPONENT_CLIENT_NAME[ctype], loginName, datas));

    pChannel->extra(loginName);

    // 向dbmgr查询用户合法性
    Network::Bundle* pBundle = Network::Bundle::createPoolObject();
    (*pBundle).newMessage(DbmgrInterface::onAccountLogin);
    (*pBundle) << loginName << password;
    (*pBundle).appendBlob(datas);
    dbmgrinfos->pChannel->send(pBundle);
}

复制代码

  这个函数进行了一系列的检查,确定合法后向dbmgr发送一个登陆请求包“(*pBundle).newMessage(DbmgrInterface::onAccountLogin);

然后在dbmgr.cpp中

复制代码

//-------------------------------------------------------------------------------------
void Dbmgr::onAccountLogin(Network::Channel* pChannel, KBEngine::MemoryStream& s) 
{
    std::string loginName, password, datas;
    s >> loginName >> password;
    s.readBlob(datas);

    if(loginName.size() == 0)
    {
        ERROR_MSG("Dbmgr::onAccountLogin: loginName is empty.\n");
        return;
    }

    pInterfacesAccountHandler_->loginAccount(pChannel, loginName, password, datas);
}

复制代码

转到interfaces_handler.cpp中

复制代码

 1 //-------------------------------------------------------------------------------------
 2 bool InterfacesHandler_Dbmgr::loginAccount(Network::Channel* pChannel, std::string& loginName,
 3                                          std::string& password, std::string& datas)
 4 {
 5     std::string dbInterfaceName = Dbmgr::getSingleton().selectAccountDBInterfaceName(loginName);
 6 
 7     thread::ThreadPool* pThreadPool = DBUtil::pThreadPool(dbInterfaceName);
 8     if (!pThreadPool)
 9     {
10         ERROR_MSG(fmt::format("InterfacesHandler_Dbmgr::loginAccount: not found dbInterface({})!\n",
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值