KBEngine warring項目源碼閱讀(二) 登錄和baseapp的負載均衡

原本不打算把登錄拿出來寫的,但是閱讀登錄部分的代碼的時候發現登錄和註冊還不太一回事,因爲登錄涉及到分配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",
11             dbInterfaceName));
12 
13         return false;
14     }
15 
16     pThreadPool->addTask(new DBTaskAccountLogin(pChannel->addr(),
17         loginName, loginName, password, SERVER_SUCCESS, datas, datas, true));
18 
19     return true;
20 }

複製代碼

點開任務DBTaskAccountLogin,基於上篇文章說過的理由只看這兩個函數

複製代碼

  1 bool DBTaskAccountLogin::db_thread_process()
  2 {
  3     // 如果Interfaces已經判斷不成功就沒必要繼續下去
  4     if(retcode_ != SERVER_SUCCESS)
  5     {
  6         ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): interfaces is failed!\n"));
  7         return false;
  8     }
  9 
 10     retcode_ = SERVER_ERR_OP_FAILED;
 11 
 12     if(accountName_.size() == 0)
 13     {
 14         ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): accountName is NULL!\n"));
 15         retcode_ = SERVER_ERR_NAME;
 16         return false;
 17     }
 18 
 19     ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
 20 
 21     if(pModule == NULL)
 22     {
 23         ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account script[{}], login[{}] failed!\n", 
 24             DBUtil::accountScriptName(), accountName_));
 25 
 26         retcode_ = SERVER_ERR_SRV_NO_READY;
 27         return false;
 28     }
 29 
 30     EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi_->name());
 31     KBEEntityLogTable* pELTable = static_cast<KBEEntityLogTable*>
 32         (entityTables.findKBETable("kbe_entitylog"));
 33 
 34     KBE_ASSERT(pELTable);
 35 
 36     KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable("kbe_accountinfos"));
 37     KBE_ASSERT(pTable);
 38 
 39     ACCOUNT_INFOS info;
 40     info.dbid = 0;
 41     info.flags = 0;
 42     info.deadline = 0;
 43 
 44     if(!pTable->queryAccount(pdbi_, accountName_, info))
 45     {
 46         flags_ = info.flags;
 47         deadline_ = info.deadline;
 48 
 49         if(ACCOUNT_TYPE(g_kbeSrvConfig.getLoginApp().account_type) != ACCOUNT_TYPE_NORMAL)
 50         {
 51             if (email_isvalid(accountName_.c_str()))
 52             {
 53                 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): account[{}] is email, autocreate failed!\n", 
 54                     accountName_));
 55 
 56                 retcode_ = SERVER_ERR_CANNOT_USE_MAIL;
 57                 return false;
 58             }
 59         }
 60 
 61         if (g_kbeSrvConfig.getDBMgr().notFoundAccountAutoCreate || 
 62             (Network::Address::NONE != g_kbeSrvConfig.interfacesAddr() && !needCheckPassword_/*第三方處理成功則自動創建賬號*/))
 63         {
 64             if(!DBTaskCreateAccount::writeAccount(pdbi_, accountName_, password_, postdatas_, info) || info.dbid == 0 || info.flags != ACCOUNT_FLAG_NORMAL)
 65             {
 66                 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): writeAccount[{}] is error!\n",
 67                     accountName_));
 68 
 69                 retcode_ = SERVER_ERR_DB;
 70                 return false;
 71             }
 72 
 73             INFO_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account[{}], autocreate successfully!\n", 
 74                 accountName_));
 75 
 76             info.password = KBE_MD5::getDigest(password_.data(), (int)password_.length());
 77         }
 78         else
 79         {
 80             ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account[{}], login failed!\n", 
 81                 accountName_));
 82 
 83             retcode_ = SERVER_ERR_NOT_FOUND_ACCOUNT;
 84             return false;
 85         }
 86     }
 87 
 88     if(info.dbid == 0)
 89         return false;
 90 
 91     if(info.flags != ACCOUNT_FLAG_NORMAL)
 92     {
 93         flags_ = info.flags;
 94         return false;
 95     }
 96 
 97     if (needCheckPassword_ || Network::Address::NONE == g_kbeSrvConfig.interfacesAddr())
 98     {
 99         if (kbe_stricmp(info.password.c_str(), KBE_MD5::getDigest(password_.data(), (int)password_.length()).c_str()) != 0)
100         {
101             retcode_ = SERVER_ERR_PASSWORD;
102             return false;
103         }
104     }
105 
106     pTable->updateCount(pdbi_, accountName_, info.dbid);
107 
108     retcode_ = SERVER_ERR_ACCOUNT_IS_ONLINE;
109     KBEEntityLogTable::EntityLog entitylog;
110     bool success = !pELTable->queryEntity(pdbi_, info.dbid, entitylog, pModule->getUType());
111 
112     // 如果有在線紀錄
113     if(!success)
114     {
115         componentID_ = entitylog.componentID;
116         entityID_ = entitylog.entityID;
117     }
118     else
119     {
120         retcode_ = SERVER_SUCCESS;
121     }
122 
123     dbid_ = info.dbid;
124     flags_ = info.flags;
125     deadline_ = info.deadline;
126     return false;
127 }
128 
129 //-------------------------------------------------------------------------------------
130 thread::TPTask::TPTaskState DBTaskAccountLogin::presentMainThread()
131 {
132     DEBUG_MSG(fmt::format("Dbmgr::onAccountLogin:loginName={0}, accountName={1}, success={2}, componentID={3}, dbid={4}, flags={5}, deadline={6}.\n", 
133         loginName_,
134         accountName_,
135         retcode_,
136         componentID_,
137         dbid_,
138         flags_,
139         deadline_
140         ));
141 
142     // 一個用戶登錄, 構造一個數據庫查詢指令並加入到執行隊列, 執行完畢將結果返回給loginapp
143     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
144     (*pBundle).newMessage(LoginappInterface::onLoginAccountQueryResultFromDbmgr);
145 
146     (*pBundle) << retcode_;
147     (*pBundle) << loginName_;
148     (*pBundle) << accountName_;
149     (*pBundle) << password_;
150     (*pBundle) << componentID_;   // 如果大於0則表示賬號還存活在某個baseapp上
151     (*pBundle) << entityID_;
152     (*pBundle) << dbid_;
153     (*pBundle) << flags_;
154     (*pBundle) << deadline_;
155     (*pBundle).appendBlob(getdatas_);
156 
157     if(!this->send(pBundle))
158     {
159         ERROR_MSG(fmt::format("DBTaskAccountLogin::presentMainThread: channel({}) not found.\n", addr_.c_str()));
160         Network::Bundle::reclaimPoolObject(pBundle);
161     }
162 
163     return DBTask::presentMainThread();
164 }

複製代碼

 

閱讀完以後知道要點是queryAccount,查詢完畢以後的調用函數是LoginappInterface::onLoginAccountQueryResultFromDbmgr

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 bool KBEAccountTableMysql::queryAccount(DBInterface * pdbi, const std::string& name, ACCOUNT_INFOS& info)
 3 {
 4     std::string sqlstr = "select entityDBID, password, flags, deadline from kbe_accountinfos where accountName=\"";
 5 
 6     char* tbuf = new char[name.size() * 2 + 1];
 7 
 8     mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(), 
 9         tbuf, name.c_str(), name.size());
10 
11     sqlstr += tbuf;
12     sqlstr += "\" or email=\"";
13     sqlstr += tbuf;
14     sqlstr += "\" LIMIT 1";
15     SAFE_RELEASE_ARRAY(tbuf);
16 
17     // 如果查詢失敗則返回存在, 避免可能產生的錯誤
18     if(!pdbi->query(sqlstr.c_str(), sqlstr.size(), false))
19         return true;
20 
21     info.dbid = 0;
22     MYSQL_RES * pResult = mysql_store_result(static_cast<DBInterfaceMysql*>(pdbi)->mysql());
23     if(pResult)
24     {
25         MYSQL_ROW arow = mysql_fetch_row(pResult);
26         if(arow != NULL)
27         {
28             KBEngine::StringConv::str2value(info.dbid, arow[0]);
29             info.name = name;
30             info.password = arow[1];
31 
32             KBEngine::StringConv::str2value(info.flags, arow[2]);
33             KBEngine::StringConv::str2value(info.deadline, arow[3]);
34         }
35 
36         mysql_free_result(pResult);
37     }
38 
39     return info.dbid > 0;
40 }

複製代碼

 

這裏十分出乎我的意料,竟然直接讀取了數據庫,按道理來說這裏讀取內存更好些。

好吧,把疑問暫且放下,我們回頭來看onLoginAccountQueryResultFromDbmgr

複製代碼

  1 //-------------------------------------------------------------------------------------
  2 void Loginapp::onLoginAccountQueryResultFromDbmgr(Network::Channel* pChannel, MemoryStream& s)
  3 {
  4     if(pChannel->isExternal())
  5         return;
  6 
  7     std::string loginName, accountName, password, datas;
  8     SERVER_ERROR_CODE retcode = SERVER_SUCCESS;
  9     COMPONENT_ID componentID;
 10     ENTITY_ID entityID;
 11     DBID dbid;
 12     uint32 flags;
 13     uint64 deadline;
 14 
 15     s >> retcode;
 16 
 17     // 登錄名既登錄時客戶端輸入的名稱, 賬號名則是dbmgr查詢得到的名稱
 18     // 這個機制用於一個賬號多名稱系統或者多個第三方賬號系統登入服務器
 19     // accountName爲本遊戲服務器賬號所綁定的終身名稱
 20     // 客戶端得到baseapp地址的同時也會返回這個賬號名稱
 21     // 客戶端登陸baseapp應該使用這個賬號名稱登陸
 22     s >> loginName;
 23     s >> accountName;
 24 
 25     s >> password;
 26     s >> componentID;
 27     s >> entityID;
 28     s >> dbid;
 29     s >> flags;
 30     s >> deadline;
 31 
 32     s.readBlob(datas);
 33 
 34     //DEBUG_MSG(fmt::format("Loginapp::onLoginAccountQueryResultFromDbmgr: loginName={}.\n",
 35     //    loginName));
 36 
 37     if((flags & ACCOUNT_FLAG_LOCK) > 0)
 38     {
 39         _loginFailed(NULL, loginName, SERVER_ERR_ACCOUNT_LOCK, datas);
 40         return;
 41     }
 42 
 43     if((flags & ACCOUNT_FLAG_NOT_ACTIVATED) > 0)
 44     {
 45         _loginFailed(NULL, loginName, SERVER_ERR_ACCOUNT_NOT_ACTIVATED, datas);
 46         return;
 47     }
 48 
 49     if(deadline > 0 && ::time(NULL) - deadline <= 0)
 50     {
 51         _loginFailed(NULL, loginName, SERVER_ERR_ACCOUNT_DEADLINE, datas);
 52         return;
 53     }
 54 
 55     PendingLoginMgr::PLInfos* infos = pendingLoginMgr_.find(loginName);
 56     if(infos == NULL)
 57     {
 58         _loginFailed(NULL, loginName, SERVER_ERR_SRV_OVERLOAD, datas);
 59         return;
 60     }
 61 
 62     // 把請求交由腳本處理
 63     SCOPED_PROFILE(SCRIPTCALL_PROFILE);
 64     PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(), 
 65                                         const_cast<char*>("onLoginCallbackFromDB"), 
 66                                         const_cast<char*>("ssHy#"), 
 67                                         loginName.c_str(),
 68                                         accountName.c_str(),
 69                                         retcode,
 70                                         datas.c_str(), datas.length());
 71 
 72     if(pyResult != NULL)
 73     {
 74         Py_DECREF(pyResult);
 75     }
 76     else
 77     {
 78         SCRIPT_ERROR_CHECK();
 79     }
 80     
 81     infos->datas = datas;
 82 
 83     Network::Channel* pClientChannel = this->networkInterface().findChannel(infos->addr);
 84     if(pClientChannel)
 85         pClientChannel->extra("");
 86 
 87     if(retcode != SERVER_SUCCESS && entityID == 0 && componentID == 0)
 88     {
 89         _loginFailed(NULL, loginName, retcode, datas);
 90         return;
 91     }
 92 
 93     // 獲得baseappmgr地址。
 94     Components::COMPONENTS& cts = Components::getSingleton().getComponents(BASEAPPMGR_TYPE);
 95     Components::ComponentInfos* baseappmgrinfos = NULL;
 96     if(cts.size() > 0)
 97         baseappmgrinfos = &(*cts.begin());
 98 
 99     if(baseappmgrinfos == NULL || baseappmgrinfos->pChannel == NULL || baseappmgrinfos->cid == 0)
100     {
101         _loginFailed(NULL, loginName, SERVER_ERR_SRV_NO_READY, datas);
102         return;
103     }
104 
105     // 如果大於0則說明當前賬號仍然存活於某個baseapp上
106     if(componentID > 0)
107     {
108         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
109         (*pBundle).newMessage(BaseappmgrInterface::registerPendingAccountToBaseappAddr);
110         (*pBundle) << componentID << loginName << accountName << password << entityID << dbid << flags << deadline << infos->ctype;
111         (*pBundle).appendBlob(infos->datas);
112         baseappmgrinfos->pChannel->send(pBundle);
113         return;
114     }
115     else
116     {
117         // 註冊到baseapp並且獲取baseapp的地址
118         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
119         (*pBundle).newMessage(BaseappmgrInterface::registerPendingAccountToBaseapp);
120 
121         (*pBundle) << loginName;
122         (*pBundle) << accountName;
123         (*pBundle) << password;
124         (*pBundle) << dbid;
125         (*pBundle) << flags;
126         (*pBundle) << deadline;
127         (*pBundle) << infos->ctype;
128         (*pBundle).appendBlob(infos->datas);
129         baseappmgrinfos->pChannel->send(pBundle);
130     }
131 }

複製代碼

好吧,戲肉來了,終於到了從BaseappMgr請求BaseApp節點的地方,看下它是怎麼做負載均衡的

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 void Baseappmgr::registerPendingAccountToBaseapp(Network::Channel* pChannel, MemoryStream& s)
 3 {
 4     std::string loginName;
 5     std::string accountName;
 6     std::string password;
 7     std::string datas;
 8     DBID entityDBID;
 9     uint32 flags;
10     uint64 deadline;
11     COMPONENT_TYPE componentType;
12 
13     s >> loginName >> accountName >> password >> entityDBID >> flags >> deadline >> componentType;
14     s.readBlob(datas);
15 
16     Components::ComponentInfos* cinfos = Components::getSingleton().findComponent(pChannel);
17     if(cinfos == NULL || cinfos->pChannel == NULL)
18     {
19         ERROR_MSG("Baseappmgr::registerPendingAccountToBaseapp: not found loginapp!\n");
20         return;
21     }
22 
23     pending_logins_[loginName] = cinfos->cid;
24 
25     updateBestBaseapp();
26 
27     if (bestBaseappID_ == 0 && numLoadBalancingApp() == 0)
28     {
29         ERROR_MSG(fmt::format("Baseappmgr::registerPendingAccountToBaseapp: Unable to allocate baseapp for load balancing! baseappSize={}, accountName={}.\n",
30             baseapps_.size(), loginName));
31     }
32 
33     ENTITY_ID eid = 0;
34     cinfos = Components::getSingleton().findComponent(BASEAPP_TYPE, bestBaseappID_);
35 
36     if (cinfos == NULL || cinfos->pChannel == NULL || cinfos->state != COMPONENT_STATE_RUN)
37     {
38         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
39         ForwardItem* pFI = new AppForwardItem();
40 
41         pFI->pBundle = pBundle;
42         (*pBundle).newMessage(BaseappInterface::registerPendingLogin);
43         (*pBundle) << loginName << accountName << password << eid << entityDBID << flags << deadline << componentType;
44         pBundle->appendBlob(datas);
45 
46         int runstate = -1;
47         if (cinfos)
48             runstate = (int)cinfos->state;
49 
50         WARNING_MSG(fmt::format("Baseappmgr::registerPendingAccountToBaseapp: not found baseapp({}, runstate={}, pChannel={}), message is buffered.\n",
51             bestBaseappID_, runstate, (cinfos && cinfos->pChannel ? cinfos->pChannel->c_str() : "NULL")));
52 
53         pFI->pHandler = NULL;
54         forward_anywhere_baseapp_messagebuffer_.push(pFI);
55         return;
56     }
57 
58     std::map< COMPONENT_ID, Baseapp >::iterator baseapps_iter = baseapps_.find(bestBaseappID_);
59 
60     DEBUG_MSG(fmt::format("Baseappmgr::registerPendingAccountToBaseapp:{}. allocBaseapp={}, numEntities={}.\n",
61         accountName, bestBaseappID_, (bestBaseappID_ > 0 ? baseapps_iter->second.numEntities() : 0)));
62     
63     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
64     (*pBundle).newMessage(BaseappInterface::registerPendingLogin);
65     (*pBundle) << loginName << accountName << password << eid << entityDBID << flags << deadline << componentType;
66     pBundle->appendBlob(datas);
67     cinfos->pChannel->send(pBundle);
68 
69     // 預先將實體數量增加
70     if (baseapps_iter != baseapps_.end())
71     {
72         baseapps_iter->second.incNumProxices();
73     }
74 }

複製代碼

 在函數updateBestBaseapp();中選取了bestBaseappID_ ,然後通過BaseappInterface::registerPendingLogin將對應的數據發給了對應的BaseApp,我們先看下這個選取過程是如何進行的。

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 void Baseappmgr::updateBestBaseapp()
 3 {
 4     bestBaseappID_ = findFreeBaseapp();
 5 }
 6 
 7 //-------------------------------------------------------------------------------------
 8 COMPONENT_ID Baseappmgr::findFreeBaseapp()
 9 {
10     std::map< COMPONENT_ID, Baseapp >::iterator iter = baseapps_.begin();
11     COMPONENT_ID cid = 0;
12 
13     float minload = 1.f;
14     ENTITY_ID numEntities = 0x7fffffff;
15 
16     for(; iter != baseapps_.end(); ++iter)
17     {
18         if ((iter->second.flags() & APP_FLAGS_NOT_PARTCIPATING_LOAD_BALANCING) > 0)
19             continue;
20         
21         // 首先進程必須活着且初始化完畢
22         if(!iter->second.isDestroyed() && iter->second.initProgress() > 1.f)
23         {
24             // 如果沒有任何實體則無條件分配
25             if(iter->second.numEntities() == 0)
26                 return iter->first;
27 
28             // 比較並記錄負載最小的進程最終被分配
29             if(minload > iter->second.load() || 
30                 (minload == iter->second.load() && numEntities > iter->second.numEntities()))
31             {
32                 cid = iter->first;
33 
34                 numEntities = iter->second.numEntities();
35                 minload = iter->second.load();
36             }
37         }
38     }
39 
40     return cid;
41 }

複製代碼

可見負載均衡策略如下:

1、首先進程必須活着且初始化完畢

2、如果沒有任何實體則無條件分配

3、比較並記錄負載最小的進程最終被分配

到這裏,我們回頭看BaseApp是如何處理BaseAppMgr發來的消息的

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 void Baseapp::registerPendingLogin(Network::Channel* pChannel, KBEngine::MemoryStream& s)
 3 {
 4     if(pChannel->isExternal())
 5     {
 6         s.done();
 7         return;
 8     }
 9 
10     std::string                                    loginName; 
11     std::string                                    accountName;
12     std::string                                    password;
13     std::string                                    datas;
14     ENTITY_ID                                    entityID;
15     DBID                                        entityDBID;
16     uint32                                        flags;
17     uint64                                        deadline;
18     COMPONENT_TYPE                                componentType;
19 
20     s >> loginName >> accountName >> password >> entityID >> entityDBID >> flags >> deadline >> componentType;
21     s.readBlob(datas);
22 
23     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
24     (*pBundle).newMessage(BaseappmgrInterface::onPendingAccountGetBaseappAddr);
25 
26     (*pBundle) << loginName;
27     (*pBundle) << accountName;
28     
29     if(strlen((const char*)&g_kbeSrvConfig.getBaseApp().externalAddress) > 0)
30     {
31         (*pBundle) << g_kbeSrvConfig.getBaseApp().externalAddress;
32     }
33     else
34     {
35         (*pBundle) << inet_ntoa((struct in_addr&)networkInterface().extaddr().ip);
36     }
37 
38     (*pBundle) << this->networkInterface().extaddr().port;
39     pChannel->send(pBundle);
40 
41     PendingLoginMgr::PLInfos* ptinfos = new PendingLoginMgr::PLInfos;
42     ptinfos->accountName = accountName;
43     ptinfos->password = password;
44     ptinfos->entityID = entityID;
45     ptinfos->entityDBID = entityDBID;
46     ptinfos->flags = flags;
47     ptinfos->deadline = deadline;
48     ptinfos->ctype = (COMPONENT_CLIENT_TYPE)componentType;
49     ptinfos->datas = datas;
50     pendingLoginMgr_.add(ptinfos);
51 }

複製代碼

做了兩件事情,

第一件是將本節點的外網IP和端口號加到消息裏發給BaseAppMgr,消息名是onPendingAccountGetBaseappAddr

第二件事情是將登陸消息加到pendingLoginMgr中

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 void Baseappmgr::onPendingAccountGetBaseappAddr(Network::Channel* pChannel, 
 3                               std::string& loginName, std::string& accountName, std::string& addr, uint16 port)
 4 {
 5     sendAllocatedBaseappAddr(pChannel, loginName, accountName, addr, port);
 6 }
 7 
 8 //-------------------------------------------------------------------------------------
 9 void Baseappmgr::sendAllocatedBaseappAddr(Network::Channel* pChannel, 
10                               std::string& loginName, std::string& accountName, const std::string& addr, uint16 port)
11 {
12     KBEUnordered_map< std::string, COMPONENT_ID >::iterator iter = pending_logins_.find(loginName);
13     if(iter == pending_logins_.end())
14     {
15         ERROR_MSG("Baseappmgr::sendAllocatedBaseappAddr: not found loginapp, pending_logins is error!\n");
16         return;
17     }
18     
19     Components::ComponentInfos* cinfos = Components::getSingleton().findComponent(iter->second);
20     if(cinfos == NULL || cinfos->pChannel == NULL)
21     {
22         ERROR_MSG("Baseappmgr::sendAllocatedBaseappAddr: not found loginapp!\n");
23         return;
24     }
25 
26     Network::Bundle* pBundleToLoginapp = Network::Bundle::createPoolObject();
27     (*pBundleToLoginapp).newMessage(LoginappInterface::onLoginAccountQueryBaseappAddrFromBaseappmgr);
28 
29     LoginappInterface::onLoginAccountQueryBaseappAddrFromBaseappmgrArgs4::staticAddToBundle((*pBundleToLoginapp), loginName, 
30         accountName, addr, port);
31 
32     cinfos->pChannel->send(pBundleToLoginapp);
33     pending_logins_.erase(iter);
34 }

複製代碼

可以看見,這裏返回了消息給loginapp

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 void Loginapp::onLoginAccountQueryBaseappAddrFromBaseappmgr(Network::Channel* pChannel, std::string& loginName, 
 3                                                             std::string& accountName, std::string& addr, uint16 port)
 4 {
 5     if(pChannel->isExternal())
 6         return;
 7     
 8     if(addr.size() == 0)
 9     {
10         ERROR_MSG(fmt::format("Loginapp::onLoginAccountQueryBaseappAddrFromBaseappmgr:accountName={}, not found baseapp, Please check the baseappmgr errorlog!\n", 
11             loginName));
12         
13         std::string datas;
14         _loginFailed(NULL, loginName, SERVER_ERR_SRV_NO_READY, datas);
15     }
16 
17     Network::Address address(addr, ntohs(port));
18 
19     DEBUG_MSG(fmt::format("Loginapp::onLoginAccountQueryBaseappAddrFromBaseappmgr:accountName={0}, addr={1}.\n", 
20         loginName, address.c_str()));
21 
22     // 這裏可以不做刪除, 仍然使其保留一段時間避免同一時刻同時登錄造成意外影響
23     PendingLoginMgr::PLInfos* infos = pendingLoginMgr_.remove(loginName);
24     if(infos == NULL)
25         return;
26     
27     infos->lastProcessTime = timestamp();
28     Network::Channel* pClientChannel = this->networkInterface().findChannel(infos->addr);
29 
30     if(pClientChannel == NULL)
31     {
32         SAFE_RELEASE(infos);
33         return;
34     }
35 
36     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
37     (*pBundle).newMessage(ClientInterface::onLoginSuccessfully);
38     uint16 fport = ntohs(port);
39     (*pBundle) << accountName;
40     (*pBundle) << addr;
41     (*pBundle) << fport;
42     (*pBundle).appendBlob(infos->datas);
43     pClientChannel->send(pBundle);
44 
45     SAFE_RELEASE(infos);
46 }

複製代碼

我們回到客戶端

複製代碼

/*
            登錄loginapp成功了
        */
        public void Client_onLoginSuccessfully(MemoryStream stream)
        {
            var accountName = stream.readString();
            username = accountName;
            baseappIP = stream.readString();
            baseappPort = stream.readUint16();
            
            Dbg.DEBUG_MSG("KBEngine::Client_onLoginSuccessfully: accountName(" + accountName + "), addr(" + 
                    baseappIP + ":" + baseappPort + "), datas(" + _serverdatas.Length + ")!");
            
            _serverdatas = stream.readBlob();
            Person p= (Person)BytesToObject (_serverdatas);
            Dbg.DEBUG_MSG("KBEngine::Client_onLoginSuccessfully: p.Age(" + p.Age + "), p.Name(" + p.Name +")!");
            login_baseapp(true);
        }

複製代碼

客戶端向對應的baseapp發送登錄請求

複製代碼

        /*
            登錄到服務端,登錄到網關(baseapp)
        */
        public void login_baseapp(bool noconnect)
        {  
            if(noconnect)
            {
                Event.fireOut("onLoginBaseapp", new object[]{});
                
                _networkInterface.reset();
                _networkInterface = new NetworkInterface();
                _networkInterface.connectTo(baseappIP, baseappPort, onConnectTo_baseapp_callback, null);
            }
            else
            {
                Bundle bundle = Bundle.createObject();
                bundle.newMessage(Message.messages["Baseapp_loginBaseapp"]);
                bundle.writeString(username);
                bundle.writeString(password);
                bundle.send(_networkInterface);
            }
        }

複製代碼

回到服務器對應的baseapp,查看loginBaseapp方法

複製代碼

  1 //-------------------------------------------------------------------------------------
  2 void Baseapp::loginBaseapp(Network::Channel* pChannel, 
  3                            std::string& accountName, 
  4                            std::string& password)
  5 {
  6     accountName = KBEngine::strutil::kbe_trim(accountName);
  7     if(accountName.size() > ACCOUNT_NAME_MAX_LENGTH)
  8     {
  9         ERROR_MSG(fmt::format("Baseapp::loginBaseapp: accountName too big, size={}, limit={}.\n",
 10             accountName.size(), ACCOUNT_NAME_MAX_LENGTH));
 11 
 12         return;
 13     }
 14 
 15     if(password.size() > ACCOUNT_PASSWD_MAX_LENGTH)
 16     {
 17         ERROR_MSG(fmt::format("Baseapp::loginBaseapp: password too big, size={}, limit={}.\n",
 18             password.size(), ACCOUNT_PASSWD_MAX_LENGTH));
 19 
 20         return;
 21     }
 22 
 23     INFO_MSG(fmt::format("Baseapp::loginBaseapp: new user[{0}], channel[{1}].\n", 
 24         accountName, pChannel->c_str()));
 25 
 26     Components::ComponentInfos* dbmgrinfos = Components::getSingleton().getDbmgr();
 27     if(dbmgrinfos == NULL || dbmgrinfos->pChannel == NULL || dbmgrinfos->cid == 0)
 28     {
 29         loginBaseappFailed(pChannel, accountName, SERVER_ERR_SRV_NO_READY);
 30         return;
 31     }
 32 
 33     PendingLoginMgr::PLInfos* ptinfos = pendingLoginMgr_.find(accountName);
 34     if(ptinfos == NULL)
 35     {
 36         loginBaseappFailed(pChannel, accountName, SERVER_ERR_ILLEGAL_LOGIN);
 37         return;
 38     }
 39     else if (!ptinfos->addr.isNone() && ptinfos->addr != pChannel->addr())
 40     {
 41         loginBaseappFailed(pChannel, accountName, SERVER_ERR_ILLEGAL_LOGIN);
 42         return;
 43     }
 44 
 45     if(ptinfos->password != password)
 46     {
 47         loginBaseappFailed(pChannel, accountName, SERVER_ERR_PASSWORD);
 48         return;
 49     }
 50 
 51     if((ptinfos->flags & ACCOUNT_FLAG_LOCK) > 0)
 52     {
 53         loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_LOCK);
 54         return;
 55     }
 56 
 57     if((ptinfos->flags & ACCOUNT_FLAG_NOT_ACTIVATED) > 0)
 58     {
 59         loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_NOT_ACTIVATED);
 60         return;
 61     }
 62 
 63     if(ptinfos->deadline > 0 && ::time(NULL) - ptinfos->deadline <= 0)
 64     {
 65         loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_DEADLINE);
 66         return;
 67     }
 68 
 69     if(idClient_.size() == 0)
 70     {
 71         ERROR_MSG("Baseapp::loginBaseapp: idClient size is 0.\n");
 72         loginBaseappFailed(pChannel, accountName, SERVER_ERR_SRV_NO_READY);
 73         return;
 74     }
 75 
 76     // 如果entityID大於0則說明此entity是存活狀態登錄
 77     if(ptinfos->entityID > 0)
 78     {
 79         INFO_MSG(fmt::format("Baseapp::loginBaseapp: user[{}] has entity({}).\n",
 80             accountName.c_str(), ptinfos->entityID));
 81 
 82         Proxy* base = static_cast<Proxy*>(findEntity(ptinfos->entityID));
 83         if(base == NULL || base->isDestroyed())
 84         {
 85             loginBaseappFailed(pChannel, accountName, SERVER_ERR_BUSY);
 86             return;
 87         }
 88         
 89         pendingLoginMgr_.removeNextTick(accountName);
 90 
 91         // 防止在onLogOnAttempt中銷燬了
 92         Py_INCREF(base);
 93 
 94         // 通知腳本異常登錄請求有腳本決定是否允許這個通道強制登錄
 95         int32 ret = base->onLogOnAttempt(pChannel->addr().ipAsString(), 
 96             ntohs(pChannel->addr().port), password.c_str());
 97 
 98         if (base->isDestroyed())
 99         {
100             Py_DECREF(base);
101 
102             loginBaseappFailed(pChannel, accountName, SERVER_ERR_OP_FAILED);
103             return;
104         }
105 
106         switch(ret)
107         {
108         case LOG_ON_ACCEPT:
109             if(base->clientMailbox() != NULL)
110             {
111                 // 通告在別處登錄
112                 Network::Channel* pOldClientChannel = base->clientMailbox()->getChannel();
113                 if(pOldClientChannel != NULL)
114                 {
115                     INFO_MSG(fmt::format("Baseapp::loginBaseapp: script LOG_ON_ACCEPT. oldClientChannel={}\n",
116                         pOldClientChannel->c_str()));
117                     
118                     kickChannel(pOldClientChannel, SERVER_ERR_ACCOUNT_LOGIN_ANOTHER);
119                 }
120                 else
121                 {
122                     INFO_MSG("Baseapp::loginBaseapp: script LOG_ON_ACCEPT.\n");
123                 }
124                 
125                 base->clientMailbox()->addr(pChannel->addr());
126                 base->addr(pChannel->addr());
127                 base->setClientType(ptinfos->ctype);
128                 base->setClientDatas(ptinfos->datas);
129                 createClientProxies(base, true);
130                 base->onGetWitness();
131             }
132             else
133             {
134                 // 創建entity的客戶端mailbox
135                 EntityMailbox* entityClientMailbox = new EntityMailbox(base->pScriptModule(), 
136                     &pChannel->addr(), 0, base->id(), MAILBOX_TYPE_CLIENT);
137 
138                 base->clientMailbox(entityClientMailbox);
139                 base->addr(pChannel->addr());
140                 base->setClientType(ptinfos->ctype);
141                 base->setClientDatas(ptinfos->datas);
142 
143                 // 將通道代理的關係與該entity綁定, 在後面通信中可提供身份合法性識別
144                 entityClientMailbox->getChannel()->proxyID(base->id());
145                 createClientProxies(base, true);
146                 base->onGetWitness();
147             }
148             break;
149         case LOG_ON_WAIT_FOR_DESTROY:
150         default:
151             INFO_MSG("Baseapp::loginBaseapp: script LOG_ON_REJECT.\n");
152             loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_IS_ONLINE);
153             Py_DECREF(base);
154             return;
155         };
156 
157         Py_DECREF(base);
158     }
159     else
160     {
161         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
162         (*pBundle).newMessage(DbmgrInterface::queryAccount);
163 
164         ENTITY_ID entityID = idClient_.alloc();
165         KBE_ASSERT(entityID > 0);
166 
167         DbmgrInterface::queryAccountArgs7::staticAddToBundle((*pBundle), accountName, password, g_componentID, 
168             entityID, ptinfos->entityDBID, pChannel->addr().ip, pChannel->addr().port);
169 
170         dbmgrinfos->pChannel->send(pBundle);
171     }
172 
173     // 記錄客戶端地址
174     ptinfos->addr = pChannel->addr();
175 }

複製代碼

這裏最重要的事情是,

1、如果存在實體,創立mailbox,並且綁定mailbox給對應的實體

2、如果不存在實體,那麼調用DbmgrInterface::queryAccount

複製代碼

 1 void Dbmgr::queryAccount(Network::Channel* pChannel, 
 2                          std::string& accountName, 
 3                          std::string& password,
 4                          COMPONENT_ID componentID,
 5                          ENTITY_ID entityID,
 6                          DBID entityDBID, 
 7                          uint32 ip, 
 8                          uint16 port)
 9 {
10     if(accountName.size() == 0)
11     {
12         ERROR_MSG("Dbmgr::queryAccount: accountName is empty.\n");
13         return;
14     }
15 
16     Buffered_DBTasks* pBuffered_DBTasks = 
17         findBufferedDBTask(Dbmgr::getSingleton().selectAccountDBInterfaceName(accountName));
18 
19     if (!pBuffered_DBTasks)
20     {
21         ERROR_MSG(fmt::format("Dbmgr::queryAccount: not found dbInterface({})!\n", 
22             Dbmgr::getSingleton().selectAccountDBInterfaceName(accountName)));
23         return;
24     }
25 
26     pBuffered_DBTasks->addTask(new DBTaskQueryAccount(pChannel->addr(), accountName, password, 
27         componentID, entityID, entityDBID, ip, port));
28 
29     numQueryEntity_++;
30 }

複製代碼

我們來看下DBTaskQueryAccount的最關鍵的兩個方法

複製代碼

  1 //-------------------------------------------------------------------------------------
  2 bool DBTaskQueryAccount::db_thread_process()
  3 {
  4     if(accountName_.size() == 0)
  5     {
  6         error_ = "accountName_ is NULL";
  7         return false;
  8     }
  9 
 10     EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi_->name());
 11     KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable("kbe_accountinfos"));
 12     KBE_ASSERT(pTable);
 13 
 14     ACCOUNT_INFOS info;
 15     info.name = "";
 16     info.password = "";
 17     info.dbid = dbid_;
 18 
 19     if(dbid_ == 0)
 20     {
 21         if(!pTable->queryAccount(pdbi_, accountName_, info))
 22         {
 23             error_ = "pTable->queryAccount() is failed!";
 24             
 25             if(pdbi_->getlasterror() > 0)
 26             {
 27                 error_ += pdbi_->getstrerror();
 28             }
 29     
 30             return false;
 31         }
 32 
 33         if(info.dbid == 0)
 34         {
 35             error_ = "dbid is 0";
 36             return false;
 37         }
 38 
 39         if(info.dbid == 0 || info.flags != ACCOUNT_FLAG_NORMAL)
 40         {
 41             error_ = "flags != ACCOUNT_FLAG_NORMAL";
 42             flags_ = info.flags;
 43             return false;
 44         }
 45 
 46         if (kbe_stricmp(info.password.c_str(), KBE_MD5::getDigest(password_.data(), (int)password_.length()).c_str()) != 0)
 47         {
 48             error_ = "password is error";
 49             return false;
 50         }
 51     }
 52 
 53     ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
 54     success_ = entityTables.queryEntity(pdbi_, info.dbid, &s_, pModule);
 55 
 56     if(!success_ && pdbi_->getlasterror() > 0)
 57     {
 58         error_ += "queryEntity: ";
 59         error_ += pdbi_->getstrerror();
 60     }
 61 
 62     dbid_ = info.dbid;
 63 
 64     if(!success_)
 65         return false;
 66 
 67     success_ = false;
 68 
 69     // 先寫log, 如果寫失敗則可能這個entity已經在線
 70     KBEEntityLogTable* pELTable = static_cast<KBEEntityLogTable*>
 71         (entityTables.findKBETable("kbe_entitylog"));
 72     KBE_ASSERT(pELTable);
 73     
 74     success_ = pELTable->logEntity(pdbi_, inet_ntoa((struct in_addr&)ip_), port_, dbid_, 
 75         componentID_, entityID_, pModule->getUType());
 76 
 77     if(!success_ && pdbi_->getlasterror() > 0)
 78     {
 79         error_ += "logEntity: ";
 80         error_ += pdbi_->getstrerror();
 81     }
 82 
 83     flags_ = info.flags;
 84     deadline_ = info.deadline;
 85 
 86     return false;
 87 }
 88 
 89 //-------------------------------------------------------------------------------------
 90 thread::TPTask::TPTaskState DBTaskQueryAccount::presentMainThread()
 91 {
 92     DEBUG_MSG(fmt::format("Dbmgr::queryAccount: {}, success={}, flags={}, deadline={}.\n", 
 93          accountName_.c_str(), success_, flags_, deadline_));
 94 
 95     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
 96     (*pBundle).newMessage(BaseappInterface::onQueryAccountCBFromDbmgr);
 97     (*pBundle) << pdbi_->dbIndex();
 98     (*pBundle) << accountName_;
 99     (*pBundle) << password_;
100     (*pBundle) << dbid_;
101     (*pBundle) << success_;
102     (*pBundle) << entityID_;
103     (*pBundle) << flags_;
104     (*pBundle) << deadline_;
105 
106     if(success_)
107     {
108         pBundle->append(s_);
109     }
110     else
111     {
112         (*pBundle) << error_;
113     }
114 
115     if(!this->send(pBundle))
116     {
117         ERROR_MSG(fmt::format("DBTaskQueryAccount::presentMainThread: channel({}) not found.\n", addr_.c_str()));
118         Network::Bundle::reclaimPoolObject(pBundle);
119     }
120 
121     return EntityDBTask::presentMainThread();
122 }

複製代碼

比對過密碼的MD5等數據後,通過onQueryAccountCBFromDbmgr消息返回對應的baseapp,

複製代碼

  1 //-------------------------------------------------------------------------------------
  2 void Baseapp::onQueryAccountCBFromDbmgr(Network::Channel* pChannel, KBEngine::MemoryStream& s)
  3 {
  4     if(pChannel->isExternal())
  5         return;
  6 
  7     std::string accountName;
  8     std::string password;
  9     bool success = false;
 10     DBID dbid;
 11     ENTITY_ID entityID;
 12     uint32 flags;
 13     uint64 deadline;
 14     uint16 dbInterfaceIndex;
 15 
 16     s >> dbInterfaceIndex >> accountName >> password >> dbid >> success >> entityID >> flags >> deadline;
 17 
 18     PendingLoginMgr::PLInfos* ptinfos = pendingLoginMgr_.remove(accountName);
 19     if(ptinfos == NULL)
 20     {
 21         ERROR_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: PendingLoginMgr not found({})\n",
 22             accountName.c_str()));
 23 
 24         s.done();
 25         return;
 26     }
 27 
 28     Proxy* base = static_cast<Proxy*>(createEntity(g_serverConfig.getDBMgr().dbAccountEntityScriptType, 
 29         NULL, false, entityID));
 30 
 31     Network::Channel* pClientChannel = this->networkInterface().findChannel(ptinfos->addr);
 32 
 33     if(!base)
 34     {
 35         ERROR_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: create {} is failed! error(base == NULL)\n",
 36             accountName.c_str()));
 37         
 38         s.done();
 39         
 40         loginBaseappFailed(pClientChannel, accountName, SERVER_ERR_SRV_NO_READY);
 41         return;
 42     }
 43 
 44     if(!success)
 45     {
 46         std::string error;
 47         s >> error;
 48         ERROR_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: query {} is failed! error({})\n",
 49             accountName.c_str(), error));
 50         
 51         s.done();
 52         
 53         loginBaseappFailed(pClientChannel, accountName, SERVER_ERR_SRV_NO_READY);
 54         
 55         this->destroyEntity(base->id(), true);
 56         return;
 57     }
 58     
 59     KBE_ASSERT(base != NULL);
 60     base->hasDB(true);
 61     base->dbid(dbInterfaceIndex, dbid);
 62     base->setClientType(ptinfos->ctype);
 63     base->setClientDatas(ptinfos->datas);
 64 
 65     PyObject* pyDict = createCellDataDictFromPersistentStream(s, g_serverConfig.getDBMgr().dbAccountEntityScriptType);
 66 
 67     PyObject* py__ACCOUNT_NAME__ = PyUnicode_FromString(accountName.c_str());
 68     PyDict_SetItemString(pyDict, "__ACCOUNT_NAME__", py__ACCOUNT_NAME__);
 69     Py_DECREF(py__ACCOUNT_NAME__);
 70 
 71     PyObject* py__ACCOUNT_PASSWD__ = PyUnicode_FromString(KBE_MD5::getDigest(password.data(), (int)password.length()).c_str());
 72     PyDict_SetItemString(pyDict, "__ACCOUNT_PASSWORD__", py__ACCOUNT_PASSWD__);
 73     Py_DECREF(py__ACCOUNT_PASSWD__);
 74 
 75     Py_INCREF(base);
 76     base->initializeEntity(pyDict);
 77     Py_DECREF(pyDict);
 78 
 79     if(pClientChannel != NULL)
 80     {
 81         // 創建entity的客戶端mailbox
 82         EntityMailbox* entityClientMailbox = new EntityMailbox(base->pScriptModule(), 
 83             &pClientChannel->addr(), 0, base->id(), MAILBOX_TYPE_CLIENT);
 84 
 85         base->clientMailbox(entityClientMailbox);
 86         base->addr(pClientChannel->addr());
 87 
 88         createClientProxies(base);
 89         
 90         /*
 91         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
 92         (*pBundle).newMessage(DbmgrInterface::onAccountOnline);
 93 
 94         DbmgrInterface::onAccountOnlineArgs3::staticAddToBundle((*pBundle), accountName, 
 95             componentID_, base->id());
 96 
 97         pChannel->send(pBundle);
 98         */
 99     }
100 
101     INFO_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: user={}, uuid={}, entityID={}, flags={}, deadline={}.\n",
102         accountName, base->rndUUID(), base->id(), flags, deadline));
103 
104     SAFE_RELEASE(ptinfos);
105     Py_DECREF(base);
106 }

複製代碼

通過

Proxy* base = static_cast<Proxy*>(createEntity(g_serverConfig.getDBMgr().dbAccountEntityScriptType,
NULL, false, entityID));

創建了對應的實體,並且創建對應的mailbox綁定上去,值得注意的是createClientProxies(base);這個函數,這個函數會將實體創建完畢的消息傳回給客戶端,並告知客戶端此通道的存在

此後腳本層的消息傳遞,就直接通過這個通道進行

複製代碼

 1 //-------------------------------------------------------------------------------------
 2 bool Baseapp::createClientProxies(Proxy* base, bool reload)
 3 {
 4     Py_INCREF(base);
 5     
 6     // 將通道代理的關係與該entity綁定, 在後面通信中可提供身份合法性識別
 7     Network::Channel* pChannel = base->clientMailbox()->getChannel();
 8     pChannel->proxyID(base->id());
 9     base->addr(pChannel->addr());
10 
11     // 重新生成一個ID
12     if(reload)
13         base->rndUUID(genUUID64());
14     
15     // 一些數據必須在實體創建後立即訪問
16     base->initClientBasePropertys();
17 
18     // 讓客戶端知道已經創建了proxices, 並初始化一部分屬性
19     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
20     (*pBundle).newMessage(ClientInterface::onCreatedProxies);
21     (*pBundle) << base->rndUUID();
22     (*pBundle) << base->id();
23     (*pBundle) << base->ob_type->tp_name;
24     //base->clientMailbox()->postMail((*pBundle));
25     base->sendToClient(ClientInterface::onCreatedProxies, pBundle);
26 
27     // 本應該由客戶端告知已經創建好entity後調用這個接口。
28     //if(!reload)
29     base->onEntitiesEnabled();
30     Py_DECREF(base);
31     return true;
32 }

複製代碼

在這裏一方面告知客戶端onCreatedProxies消息,一方面進行腳本的onEntitiesEnabled消息的回調

我們回到unity端,閱讀對應的代碼

複製代碼

/*
            服務端通知創建一個角色
        */
        public void Client_onCreatedProxies(UInt64 rndUUID, Int32 eid, string entityType)
        {
            Dbg.DEBUG_MSG("KBEngine::Client_onCreatedProxies: eid(" + eid + "), entityType(" + entityType + ")!");
            
            entity_uuid = rndUUID;
            entity_id = eid;
            entity_type = entityType;
            
            if(!this.entities.ContainsKey(eid))
            {
                ScriptModule module = null;
                if(!EntityDef.moduledefs.TryGetValue(entityType, out module))
                {
                    Dbg.ERROR_MSG("KBEngine::Client_onCreatedProxies: not found module(" + entityType + ")!");
                    return;
                }
                
                Type runclass = module.script;
                if(runclass == null)
                    return;
                
                Entity entity = (Entity)Activator.CreateInstance(runclass);
                entity.id = eid;
                entity.className = entityType;
                
                entity.baseMailbox = new Mailbox();
                entity.baseMailbox.id = eid;
                entity.baseMailbox.className = entityType;
                entity.baseMailbox.type = Mailbox.MAILBOX_TYPE.MAILBOX_TYPE_BASE;

                entities[eid] = entity;
                
                MemoryStream entityMessage = null;
                _bufferedCreateEntityMessage.TryGetValue(eid, out entityMessage);
                
                if(entityMessage != null)
                {
                    Client_onUpdatePropertys(entityMessage);
                    _bufferedCreateEntityMessage.Remove(eid);
                    entityMessage.reclaimObject();
                }
                
                entity.__init__();
                entity.inited = true;
                
                if(_args.isOnInitCallPropertysSetMethods)
                    entity.callPropertysSetMethods();
            }
            else
            {
                MemoryStream entityMessage = null;
                _bufferedCreateEntityMessage.TryGetValue(eid, out entityMessage);
                
                if(entityMessage != null)
                {
                    Client_onUpdatePropertys(entityMessage);
                    _bufferedCreateEntityMessage.Remove(eid);
                    entityMessage.reclaimObject();
                }
            }
        }

複製代碼

客戶端也進行了mailbox的綁定,並且回調了對應entity的init方法,account的entity是account.cs,點開看下

        public override void __init__()
        {
            Event.fireOut("onLoginSuccessfully", new object[]{KBEngineApp.app.entity_uuid, id, this});
            baseCall("reqAvatarList", new object[0]);
        }

這裏開始,請求角色列表,放到下一篇文章吧。

我這篇文章拷貝黏貼了大量的代碼,C++的代碼十分臃腫,KBE已經進行了大量的封裝,使其更接近邏輯的本質。

我是青島遠碩信息科技發展有限公司的Peter,如果轉載的話,請保留這段文字。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章