bigworld源码分析(2)—— loginApp分析

  loginApp是整个bigworld进行用户认证的服务,是用户进入游戏的第一步。本篇主要针对loginApp的认证流程,如何和其他服务进行交互,以及loginApp针对多服务负载的不同做法进行分析。

  1. loginApp用户认证流程

  用户通过loginApp认证,主要是以下几个步骤:

  (1) client发送认证请求(前提是他已经知道loginApp的ip和端口)

  (2) loginApp在其监听端口上,收到这个请求

  (3) 收到请求之后,loginApp将认证信息转发给DBMgr,通过DBMgr来验证登陆认证是否合法

  (4) DBMgr通过查询数据库来进行确认

  (5) 如果认证是合法的,那么DBMgr通知BaseAppMgr去创建一个新的entity(玩家实例)

  (6) BaseAppMgr将创建新entity的需求,转发给当前负载最小的BaseApp

  (7) BaseApp创建一个新的Proxy,Proxy就是BaseApp上代表用户的实例

  (8) 同时可能需要在CellApp中创建一个玩家entity,这个要根据实际的项目需要了。也有可能直到用户选取了角色之后,才在cellApp中创建entity。

  (9) 当上门的事情都做完之后,proxy的UDP端口将结果返回给client(通过BaseAppMgr,DBMgr,最后LoginApp)

  上述流程的示意图是这样的:

  

  2. loginApp认证流程在代码中的体现

  代码只截取认为最重要的部分和关键语句,大部分代码省略,使用//......来代替

  (2)(3)loginApp收到认证请求,并且转发:

  

  
  // 先针对参数有效性进行检查
  // ......

  // 判断是不是上一个认证请求还没做完
  //
First check whether this is a repeat attempt from a recent pending // login before attempting to decrypt and log in. if (this->handleResentPendingAttempt( source, header.replyID )) { // ignore this one, it's in progress loginStats_.incPending(); return; }
// 判断是否是否了加密 #ifdef USE_OPENSSL Mercury::PublicKeyCipher
* pPrivateKey = &privateKey_; #else Mercury::PublicKeyCipher * pPrivateKey = NULL; #endif //.......
  // 判断是否之前有认证cache
// First check whether this is a repeat attempt from a recent // resolved login before attempting to log in. if (this->handleResentCachedAttempt( source, pParams, header.replyID )) { // ignore this one, we've seen it recently return; } // ...... INFO_MSG( "Logging in %s{%s} (%s) ", pParams->username().c_str(), pParams->password().c_str(), source.c_str() ); // Remember that this attempt is now in progress and discard further // attempts from that address for some time after it completes. cachedLoginMap_[ source ].reset(); cachedLoginMap_[ source ].pParams( pParams ); DatabaseReplyHandler * pDBHandler = new DatabaseReplyHandler( source, header.replyID, pParams );   
  
  // 发送给DBMgr Mercury::Bundle
& dbBundle = this->dbMgr().bundle(); dbBundle.startRequest( DBInterface::logOn, pDBHandler ); dbBundle << source << false /*off channel*/ << *pParams; this->dbMgr().send();

  

  (9) 认证消息返回给client代码

 void DatabaseReplyHandler::handleMessage(    const Mercury::Address & /*source*/,    Mercury::UnpackedMessageHeader & header,    BinaryIStream & data,

void * /*arg*/ )
{
    uint8 status;
    data >> status;
    
  //
查认证返回码
if (status != LogOnStatus::LOGGED_ON) {
    //
如果认证失败
    // ......
} LoginReplyRecord lrr; data >> lrr; // If the client has an external address, send them the firewall // address instead! if (!LoginApp::instance().netMask().containsAddress( clientAddr_.ip )) { INFO_MSG( "DatabaseReplyHandler::handleMessage: " "Redirecting external client %s to firewall. ", clientAddr_.c_str() ); lrr.serverAddr.ip = LoginApp::instance().externalIP(); }   
  // 返回client认证结果,并且缓存认证结果

  LoginApp::instance().sendAndCacheSuccess( clientAddr_, replyID_, lrr, pParams_ ); gNumLogins
++; delete this; }

  3. loginApp是如何做负载的

  loginApp是bigworld中用来处理用户登录认证的,为了防止单一loginApp存在性能瓶颈,同时防止单点故障,bigworld也是可以运行同时运行多个loginApp。不过我们知道多个baseApp和cellApp是分别有,baseAppMgr和cellAppMgr来做管理和负载的,loginApp我们却没有找到类似的loginAppMgr。

  在bigworld的文档中,是这样写的:

In terms of load, a single LoginApp should be sufficient for most purposes. However, as this is still a potential bottleneck, and single point of failure, BigWorld supports running multiple LoginApps.
The remaining issue then is how to distribute the client login requests across multiple login servers. The standard way to do this is use a DNS solution similar to how popular web sites balance traffic load across  multiple machines.

  从上面的介绍来看,bigworld本身确实是没有提供类似的loginAppMgr,而是需要我们使用DNS来做负载,DNS返回不同的loginApp ip,来分担压力。

原文地址:https://www.cnblogs.com/chobits/p/5075755.html