副本集源码实现
1. 启动流程
db.cpp
main
mongoDbMain
initAndListen
_initAndListen
Listen
createServer(options, new MyMessageHandler() );
startReplication
这里主要分析startReplication函数
2.startReplication
boost::thread t( boost::bind( &startReplSets, replSetCmdline) ); 启动一个线程
startReplSets
ReplSet::make(*replSetCmdline))->go();
Make
Init
loadConfig();
此函数主要作用:等待副本集所依赖的成员(端口)的启动,等待初始化
初始化后启动一个线程,定时连接其他节点,作为心跳检测,
_go
loadLastOpTimeWritten 获取oplog.rs 表的最后同步时间
{ ts: Timestamp 1373520206000|1, h: 0, v: 2, op: "n", ns: "", o: { msg: "initiating set" } }
startThreads(); 下面重点分析
newReplUp(); 为_logOp函数指针绑定 函数,cud操作都会使用该函数先锁local库
startThreads 函数主体部分如下
void ReplSetImpl::startThreads() {
task::fork(mgr);
mgr->send( boost::bind(&Manager::msgCheckNewState, theReplSet->mgr) );
if (myConfig().arbiterOnly) {
return;
}
boost::thread t(startSyncThread);
replset::BackgroundSync* sync = replset::BackgroundSync::get();
boost::thread producer(boost::bind(&replset::BackgroundSync::producerThread, sync));
boost::thread notifier(boost::bind(&replset::BackgroundSync::notifierThread, sync));
task::fork(ghost);
// member heartbeats are started in ReplSetImpl::initFromConfig
}
这里主要就是3个线程一个函数,startSyncThread,producerThread,notifierThread,msgCheckNewState下面了解一下这3个线程的各自的作用和函数msgCheckNewState的作用
(1)msgCheckNewState 节点状态切换,投票选举等,这里就不详细分析
(2)producerThread 线程分析(只有从节点才起该线程)
_producerThread
Produce
getOplogReader 获取一个同步数据节点
tailingQueryGTE 获取同步节点的oplog数据
{ 把同步到的oplog数据存入buffer中
BSONObj o = r.nextSafe().getOwned();
_buffer.push(o);
}
(3) startSyncThread 线程分析
while(1)
_syncThread 下面详见主体部分
void ReplSetImpl::_syncThread() {
// Check criteria for doing an initial sync:
// 1. If the oplog is empty, do an initial sync
// 2. If minValid has _initialSyncFlag set, do an initial sync
if (lastOpTimeWritten.isNull() || getInitialSyncFlag()) {
syncDoInitialSync();
return; // _syncThread will be recalled, starts from top again in case sync failed.
}
/* we have some data. continue tailing. */
replset::SyncTail tail(replset::BackgroundSync::get());
tail.oplogApplication();
}
函数加粗部分的说明已经说明了函数的作用,就是从主节点获取oplog
tail.oplogApplication();
multiApply 此函数把前一个producerThread 线程读取的日志写入自身数据库
applyOpsToOplog 更新同步的最新时间戳
(3)notifierThread (比较不好理解)大概意思就是通知服务端,我们已经成功接同步数据了
总结: 副本集 的实现原理,就是一个线程不停的进行心跳检测及节点状态改变投票选举
一个线程从服务节点(不一定是主节点)获取oplog数据放到自身的buffer中
一个线程从自身的buffer中把数据同步到自身的数据库中
一个线程通知服务节点数据同步完毕
线程之间的配合用到了wait,notify机制