日期:2014-05-16 浏览次数:20504 次
最近工作需要开始研究mongoDB,我准备从其源代码角度,对于mongod和mongos服务的架构、sharding策略、replicaset策略、数据同步容灾、索引等机制做一个本质性的了解。其代码约20万行(我研究的是 2.0.6版本源码),本篇先从mongod的启动流程说起,它本是一个多线程程序,所以本文在于说明mongod有多少个线程,每个线程的意义所在。希望大家阅读本文时关注在mongod的外围框架,暂不涉及数据文件的组织、索引B树的组织等,仅focus in在网络框架、线程模型上。
弄清楚这点的好处很明显:之后就可以有的放矢的研究mongod某个模块究竟是如何实现的,可以快速的跳到相应的类中阅读源码,解决我们在产品中的实际问题。我认为这是研究其庞大源码一个好的开始。
在说明mongod前,须了解mongoDB大量代码是基于boost库构建的,因此这里先行对boost库建立线程做个简单的了解。
1、boost库如何建立线程
boost::thread是boost中跨平台的多线程库,mongoDB创建线程时大多数情况下是使用thread库的(少量情况直接调用pthread_create方法),主要使用了以下两种方式:
(1)直接运行让线程运行func
例如durThread线程:
void durThread() {
while( !inShutdown() ) { ... }
}
boost::thread t(durThread);
(2)在类中定义静态的run方法,调用thread创建线程
class FileAllocator : boost::noncopyable {
static void run( FileAllocator * fa );
void FileAllocator::start() {
boost::thread t( boost::bind( &FileAllocator::run , this ) );
}
};
2、mongod的入口
mongod的入口main函数在src/mongo/db/db.cpp文件中,我画了个简单的活动图简要介绍其启动流程:
如上图所示,这里出现了12个固定线程,还没有包括mongod运行以后处理请求时派生出来的线程,如下所示:
– interruptThread
– DataFileSync::run
– FileAllocator::run
– durThread
– SnapshotThread::run
– ClientCursorMonitor::run
– PeriodicTask::Runner::run
– TTLMonitor::run
– replSlaveThread
– replMasterThread
– webServerThread
– 处理数据库请求的主线程
如果不属于任何replica set,那么至少有10个固定线程(去除 replSlaveThread和 replMasterThread)。
下面我们先讨论这10个固定的线程,再讨论性能非常弱的监听web事件的线程是怎样处理请求的,最后讨论性能稍好一点的主服务线程是怎样处理请求的。
3、5个基于BackgroundJob类实现的工作线程
这5个线程分别是DataFileSync,SnapshotThread, ClientCursorMonitor, TTLMonitor, PeriodicTask,类图如下所示:
上面这5个类也是用boost::threadfunction方法创建线程运行的,它们继承了BackgroundJob类,使用go方法启动线程执行jobBody就是在启动线程执行run方法,如下所示:
BackgroundJob& BackgroundJob::go() { boost::thread t( boost::bind( &BackgroundJob::jobBody , this, _status ) ); return *this; } void BackgroundJob::jobBody( boost::shared_ptr<JobStatus> status ) { ... run(); ... }
DataFileSync主要在调用MemoryMappedFile::flush方法将内存中的数据刷到磁盘上。 我们知道,mongodb是调用mmap把磁盘中的数据映射到内存中的,所以必须有一个机制时刻的刷数据到硬盘才能保证可靠性,多久刷一次是与syncdelay参数相关的。
SnapshotThread将生成快照文件帮助快速恢复。
ClientCursorMonitor将管理用户的游标,每4秒调用一次idleTimeReport()方法,每一分钟调用sayMemoryStatus()方法。
TTLMonitor管理TTL,通过调用doTTLForDB()方法检查所有db。
PeriodicTask将从动态数组std::vector<PeriodicTask* > _tasks中获取周期性任务执行。
4、5个直接提供全局方法执行的线程
FileAllocator用于分配新文件,它决定分配文件的大小,例如用翻倍的方式。
interruptThread只处理信号量。
durThread做批量提交和回滚工作。
replSlaveThread是当前结点作为secondary时的同步线程。
replMasterThread是当前结点作为master时的同步线程。
5、web监听线程
mongod是如何处理web请求的呢?它是通过网络框架中的核心类Listerner实现的,类图如下所示:
怎么理解这幅类图呢?
首先看 Listener类,它负责监听、创建新连接,其工作步骤如下:
a、创