SequoiaDB 系列之五 :源码分析之main函数

好久好久没有写博客了,因为一直要做各种事,工作上的,生活上的,这一下就是半年。

时光如梭。

这两天回头看了看写的博客,感觉都是贻笑大方。

但是还是想坚持把SequoiaDB系列写完。

 

初步的打算已经确定好,已经更新的 前言 中。

从本篇开始,进入源码分析篇。

为了能让自己坚持下去,也让看我的博客学习的同学由浅入深逐步学习,我们先从简单的开始。

如果你觉得本系列的博文让你觉得有用,请收藏我的博客地址 :)

 

分析SequoiaDB的进程模型,免不了要从进程的Main函数开涮。

SequoiaDB源码编译出来之后,主要的进程就是一个,在bin目录下的sequoiadb。

这个执行程序,能根据配置文件,化身成不用的角色,比如coord节点进程,data节点进程,以及catalog节点进程。

sequoiadb的主函数入口,代码位于 SequoiaDB/engine/pmd/pmdMain.cpp 中,非常简单,堪比“Hello world”:

INT32 main ( INT32 argc, CHAR** argv )
{
   INT32 rc = SDB_OK ;
   PD_TRACE_ENTRY ( SDB_PMDMAIN );
   rc = engine::pmdMasterThreadMain ( argc, argv ) ;
   PD_TRACE_EXITRC ( SDB_PMDMAIN, rc );
   return rc ;
}

main函数的函数体,其实就是执行engine::omdMasterThreadMain函数,在函数退出的时候,取得执行后的错误码结束进程。

 

SequoiaDB的源码中,C和C++混合存在。源码中定义了不少宏,像 PD_TRACE_ENTRY,PD_TRACE_EXITRC等等,这类函数只要是检测用的,我就不表述了,有兴趣的自己去研究一下 : )

接下来我们来看pmdMasterThreadMain函数:

  1 INT32 pmdMasterThreadMain ( INT32 argc, CHAR** argv )
  2 {
  3    INT32      rc       = SDB_OK ;
  4    PD_TRACE_ENTRY ( SDB_PMDMSTTHRDMAIN );
  5    pmdKRCB   *krcb     = pmdGetKRCB () ;
  6    UINT32     startTimerCount = 0 ;
  7 
  8    rc = pmdResolveArguments ( argc, argv ) ;
  9    if ( rc )
 10    {
 11       ossPrintf( "Failed resolving arguments(error=%d), exit"OSS_NEWLINE,
 12                  rc ) ;
 13       goto error ;
 14    }
 15    if ( PMD_IS_DB_DOWN )
 16    {
 17       return rc ;
 18    }
 19 
 20    sdbEnablePD( pmdGetOptionCB()->getDiagLogPath(),
 21                 pmdGetOptionCB()->diagFileNum() ) ;
 22    setPDLevel( (PDLEVEL)( pmdGetOptionCB()->getDiagLevel() ) ) ;
 23    // 设置log日志级别,以免输出不关心的日志
 24    PD_LOG ( ( getPDLevel() > PDEVENT ? PDEVENT : getPDLevel() ) ,
 25             "Start sequoiadb(%s) [Ver: %d.%d, Release: %d, Build: %s]...",
 26             pmdGetOptionCB()->krcbRole(), SDB_ENGINE_VERISON_CURRENT,
 27             SDB_ENGINE_SUBVERSION_CURRENT, SDB_ENGINE_RELEASE_CURRENT,
 28             SDB_ENGINE_BUILD_TIME ) ;
 29 
 30    {
 31       BSONObj confObj ;
 32       krcb->getOptionCB()->toBSON( confObj ) ;
 33       PD_LOG( PDEVENT, "All configs: %s", confObj.toString().c_str() ) ;
 34    }
 35    // 捕捉操作系统信号
 36    rc = pmdEnableSignalEvent( pmdGetOptionCB()->getDiagLogPath(),
 37                               (PMD_ON_QUIT_FUNC)pmdOnQuit ) ;
 38    PD_RC_CHECK ( rc, PDERROR, "Failed to enable trap, rc: %d", rc ) ;
 39    // 根据role类型,注册不同的功能模块
 40    sdbGetPMDController()->registerCB( pmdGetDBRole() ) ;
 41    // 启动分析
 42    rc = _pmdSystemInit() ;
 43    if ( rc )
 44    {
 45       goto error ;
 46    }
 47    // 初始化各个功能模块
 48    rc = krcb->init() ;
 49    if ( rc )
 50    {
 51       PD_LOG( PDERROR, "Failed to init krcb, rc: %d", rc ) ;
 52       goto error ;
 53    }
 54 
 55    rc = _pmdPostInit() ;
 56    if ( rc )
 57    {
 58       goto error ;
 59    }
 60    // 进入while循环,等待收到功能都完成初始化,可以提供服务的通知
 61    while ( PMD_IS_DB_UP && startTimerCount < PMD_START_WAIT_TIME &&
 62            !krcb->isBusinessOK() )
 63    {
 64       ossSleepmillis( 100 ) ;
 65       startTimerCount += 100 ;
 66    }
 67 
 68    if ( PMD_IS_DB_DOWN )
 69    {
 70       rc = krcb->getExitCode() ;
 71       PD_LOG( PDERROR, "Start failed, rc: %d", rc ) ;
 72       goto error ;
 73    }
 74    else if ( startTimerCount >= PMD_START_WAIT_TIME )
 75    {
 76       PD_LOG( PDWARNING, "Start warning (timeout)" ) ;
 77    }
 78 
 79 #if defined (_LINUX)
 80    {
 81       CHAR pmdProcessName [ OSS_RENAME_PROCESS_BUFFER_LEN + 1 ] = {0} ;
 82       ossSnprintf ( pmdProcessName, OSS_RENAME_PROCESS_BUFFER_LEN,
 83                     "%s(%s) %s", utilDBTypeStr( pmdGetDBType() ),
 84                     pmdGetOptionCB()->getServiceAddr(),
 85                     utilDBRoleShortStr( pmdGetDBRole() ) ) ;
 86       ossEnableNameChanges ( argc, argv ) ;
 87       ossRenameProcess ( pmdProcessName ) ;
 88    }
 89 #endif // _LINUX
 90    {
 91       EDUID agentEDU = PMD_INVALID_EDUID ;
 92       pmdEDUMgr *eduMgr = pmdGetKRCB()->getEDUMgr() ;
 93       eduMgr->startEDU ( EDU_TYPE_PIPESLISTENER,
 94                          (void*)pmdGetOptionCB()->getServiceAddr(),
 95                          &agentEDU ) ;
 96       eduMgr->regSystemEDU ( EDU_TYPE_PIPESLISTENER, agentEDU ) ;
 97    }
 98    // 大while循环,如果程序没有收到退出信号,就一直在while中;收到退出信号,PMD_IS_DB_UP所代表的变量就会变成 FALSE
 99    while ( PMD_IS_DB_UP )
100    {
101       ossSleepsecs ( 1 ) ;
102       sdbGetPMDController()->onTimer( OSS_ONE_SEC ) ;
103    }
104    rc = krcb->getExitCode() ;
105 
106 done :
107    PMD_SHUTDOWN_DB( rc ) ;
108    pmdSetQuit() ;
109    krcb->destroy () ;
110    pmdGetStartup().final() ;
111    PD_LOG ( PDEVENT, "Stop sequoiadb, exit code: %d",
112             krcb->getExitCode() ) ;
113    PD_TRACE_EXITRC ( SDB_PMDMSTTHRDMAIN, rc );
114    return utilRC2ShellRC( rc ) ;
115 error :
116    goto done ;
117 }

 

看起来有点大,慢慢来。

首先,函数通过 pmdGetKRCB() 得到了一个krcb的对象指针。所谓krcb,其实全面大概就是 kernel control block了。如果你跟进它的产生里面,你会发现它是 static的,全局的静态变量。基本上可以确定,这个是数据库的一个核心模块。这里我们先不管。

 

接下来,对入参进行解析,通过pmdResolveArguments函数,main函数很简单,只是简单地把程序的附加参数,传给了pmdMasterThreadMain,然后在这个地方解析。

再就是sdbEnablePD,setPDLevel等,这些是和打印程序运行中的一些关键信息相关的,比如日志等级啊,日志文件路径等等。

这里,我们不关心这些。

 

再下来就是用pmdEnableSignalEvent函数处理操作系统信号:当收到操作系统发给进程信号的时候,catch到信号事件,然后对针对信号做一些处理的。一个好的服务端程序,是应该能catch到信号事件,然后对信号做一些处理的。例如,程序跑着跑着,收到一个SIGPIPE信号,如果没有捕捉到信号,程序就退出了。没有留下任何帮助信息。这个时候,如果能捕捉到这个信号,抓取进程的栈数据,放倒一个文件里面,就可以根据这些信息,去定位程序出问题的地方。很多程序的dump收集,就是基于这个原理的。感兴趣的可以跟进 pmdEnableSignalEvent函数,看看它怎么捕捉信号事件,怎么处理的。

 

接下来就到了重点了:

sdbGetPMDController()->registerCB( pmdGetDBRole() ) ;

_pmdSystemInit() ;

krcb->init() ;

这几个函数,主要是根据当前进程的角色,用来初始化已经注册的引擎模块。

 

先看注册:

 1 void _pmdController::registerCB( SDB_ROLE dbrole )
 2 {
 3    // 根据不同的数据节点角色,注册对应的模块
 4    // DPS   数据保护服务模块,这块的核心是记录写操作的日志,方便同步,如果你对mongodb熟悉的话,有个oplog,功能和它类似,它是一致性的保证之一
 5    // TRANS 事务功能模块
 6    // CLS   集群管理服务模块,管理集群中的节点
 7    // BPS   
 8    // FMP   外部消息协议模块
 9    // CATALOGUE 编目信息服务模块
10    // AUTH  鉴权模块
11    // DMS   数据管理服务模块,这是数据库存储的核心
12    // SQL   sql语言支持模块
13    // RTN   平台运行库,主要是跨平台的api封装
14    // OMSVC OM服务,支持rest等协议支持模块
15    // AGGR  数据聚集服务模块
16    if ( SDB_ROLE_DATA == dbrole )
17    {
18       PMD_REGISTER_CB( sdbGetDPSCB() ) ;        // DPS
19       PMD_REGISTER_CB( sdbGetTransCB() ) ;      // TRANS
20       PMD_REGISTER_CB( sdbGetClsCB() ) ;        // CLS
21       PMD_REGISTER_CB( sdbGetBPSCB() ) ;        // BPS
22    }
23    else if ( SDB_ROLE_COORD == dbrole )
24    {
25       PMD_REGISTER_CB( sdbGetTransCB() ) ;      // TRANS
26       PMD_REGISTER_CB( sdbGetCoordCB() ) ;      // COORD
27       PMD_REGISTER_CB( sdbGetFMPCB () ) ;       // FMP
28    }
29    else if ( SDB_ROLE_CATALOG == dbrole )
30    {
31       PMD_REGISTER_CB( sdbGetDPSCB() ) ;        // DPS
32       PMD_REGISTER_CB( sdbGetTransCB() ) ;      // TRANS
33       PMD_REGISTER_CB( sdbGetClsCB() ) ;        // CLS
34       PMD_REGISTER_CB( sdbGetCatalogueCB() ) ;  // CATALOGUE
35       PMD_REGISTER_CB( sdbGetBPSCB() ) ;        // BPS
36       PMD_REGISTER_CB( sdbGetAuthCB() ) ;       // AUTH
37    }
38    else if ( SDB_ROLE_STANDALONE == dbrole )
39    {
40       PMD_REGISTER_CB( sdbGetDPSCB() ) ;        // DPS
41       PMD_REGISTER_CB( sdbGetTransCB() ) ;      // TRANS
42       PMD_REGISTER_CB( sdbGetBPSCB() ) ;        // BPS
43    }
44    else if ( SDB_ROLE_OM == dbrole )
45    {
46       PMD_REGISTER_CB( sdbGetDPSCB() ) ;        // DPS
47       PMD_REGISTER_CB( sdbGetTransCB() ) ;      // TRANS
48       PMD_REGISTER_CB( sdbGetBPSCB() ) ;        // BPS
49       PMD_REGISTER_CB( sdbGetAuthCB() ) ;       // AUTH
50       PMD_REGISTER_CB( sdbGetOMManager() ) ;    // OMSVC
51    }
52    PMD_REGISTER_CB( sdbGetDMSCB() ) ;           // DMS
53    PMD_REGISTER_CB( sdbGetRTNCB() ) ;           // RTN
54    PMD_REGISTER_CB( sdbGetSQLCB() ) ;           // SQL
55    PMD_REGISTER_CB( sdbGetAggrCB() ) ;          // AGGR
56    PMD_REGISTER_CB( sdbGetPMDController() ) ;   // CONTROLLER
57 }

以上表明,数据库节点的角色,分为data,calalog,coord,om,和standalone等。不同的角色,会注册(加载)不同的功能模块。

 

再看_pmdSystemInit函数,这个函数会初始化系统模块:

 1 static INT32 _pmdSystemInit()
 2 {
 3    INT32 rc = SDB_OK ;
 4 
 5    rc = pmdGetStartup().init( pmdGetOptionCB()->getDbPath() ) ;
 6    PD_RC_CHECK( rc, PDERROR, "Start up check failed[rc:%d]", rc ) ;
 7 
 8    rc = getQgmStrategyTable()->init() ;
 9    PD_RC_CHECK( rc, PDERROR, "Init qgm strategy table failed, rc: %d",
10                 rc ) ;
11 
12 done:
13    return rc ;
14 error:
15    goto done ;
16 }

这是函数从指定的路径中读取启动文件并初始化,然后初始化SQL相关的策略。启动文件是一个隐藏文件,当数据库正常或者异常退出时候,会记录数据库的状态。如果是从异常退出,则在再次启动的时候,会重新找主节点做全量同步,是自身数据和主节点一致。至于SQL相关的策略,我没有细看,应该是SQL语法树相关。

 

在完成启动分析之后,接下来就开始初始化前面注册的功能模块了,krcb->init() 

 1 INT32 _SDB_KRCB::init ()
 2 {
 3    INT32 rc = SDB_OK ;
 4    INT32 index = 0 ;
 5    IControlBlock *pCB = NULL ;
 6 
 7    _mainEDU.setName( "Main" ) ;
 8    if ( NULL == pmdGetThreadEDUCB() )
 9    {
10       pmdDeclareEDUCB( &_mainEDU ) ;
11    }
12 
13    rc = ossGetHostName( _hostName, OSS_MAX_HOSTNAME ) ;
14    PD_RC_CHECK( rc, PDERROR, "Failed to get host name, rc: %d", rc ) ;
15 
16    _init = TRUE ;
17    // 一次初始化已经注册的功能模块
18    for ( index = 0 ; index < SDB_CB_MAX ; ++index )
19    {
20       pCB = _arrayCBs[ index ] ;
21       if ( !pCB )
22       {
23          continue ;
24       }
25       if ( SDB_OK != ( rc = pCB->init() ) )
26       {
27          PD_LOG( PDERROR, "Init cb[Type: %d, Name: %s] failed, rc: %d",
28                  pCB->cbType(), pCB->cbName(), rc ) ;
29          goto error ;
30       }
31    }
32    // 依次激活已经初始化的功能模块
33    for ( index = 0 ; index < SDB_CB_MAX ; ++index )
34    {
35       pCB = _arrayCBs[ index ] ;
36       if ( !pCB )
37       {
38          continue ;
39       }
40       if ( SDB_OK != ( rc = pCB->active() ) )
41       {
42          PD_LOG( PDERROR, "Active cb[Type: %d, Name: %s] failed, rc: %d",
43                  pCB->cbType(), pCB->cbName(), rc ) ;
44          goto error ;
45       }
46    }
47 
48    _isActive = TRUE ;
49    // 时间采样,不表
50    _curTime.sample() ;
51 
52 done:
53    return rc ;
54 error:
55    goto done ;
56 }

前面有提到 krcb是一个全局的变量,是整个数据库的核心。 SequoiaDB中的各个模块,都继承自同一个控制接口 IControlBlock,由虚函数来实现多态,并且交给KRCB模块集中管理,体现了软件开发中“谁产生,谁管理”的思想。

 1 class _IControlBlock : public SDBObject, public _ISDBRoot
 2 {
 3 public:
 4    _IControlBlock () {}
 5    virtual ~_IControlBlock () {}
 6 
 7    virtual SDB_CB_TYPE cbType() const = 0 ;
 8    virtual const CHAR* cbName() const = 0 ;
 9 
10    virtual INT32  init () = 0 ;
11    virtual INT32  active () = 0 ;
12    virtual INT32  deactive () = 0 ;
13    virtual INT32  fini () = 0 ;
14    virtual void   onConfigChange() {}
15 
16 } ;
17 typedef _IControlBlock IControlBlock ;

函数中通过for循环,初始化各个模块,然后并激活各个模块。如此,数据库就开始真正的提供服务了,_isActive = TRUE很坦白地说明了这一点。

 

至于最后的_pmdPostInit() 函数,就是给初始化工作做一些扫尾工作,具体是把异常启动的standalone模式的节点或提供om服务节点的节点状态标记为正常。具体内容就不再贴代码了。

 

然后,程序就开始做一些启动后的扫尾工作:重命名进程,以便ps命令看到整齐的进程角色和服务端口;创建PIPE监听的服务,只是用于检测数据库状态,(载体是EDU)。

然后主线程进入了while循环,直到收到退出信号:

1 while ( PMD_IS_DB_UP )
2 {
3    ossSleepsecs ( 1 ) ;
4    sdbGetPMDController()->onTimer( OSS_ONE_SEC ) ;
5 }

 

至此,我们的main函数分析到一段落。

 

从整个main函数的分析,可以看出SequoiaDB中的功能划分很清楚。一个KRCB控制块,管理其他功能模块(也是控制块);其他模块,管理自身下的功能。以后会分析到几个主要模块的功能,如DPS,DMS等。

这和我认识的软件开发思想大致相同:一个软件产品,从粗粒度来看,无非就是功能模块的组装,但是要合理地去协调各个模块的关系,使之井然有序稳定地工作,这就关系到技术细节上了。

 

感谢您能耐心看到这里。

下一篇开始分析SequoiaDB的插入以及相关源码。

 

=====>THE END<===== 

你可能感兴趣的:(SequoiaDB 系列之五 :源码分析之main函数)