1. 程式人生 > >Mongodb原始碼分析--Replication之主從模式--Slave

Mongodb原始碼分析--Replication之主從模式--Slave

    在上文中介紹了主從(master-slave)模式下的一些基本概念及master的執行流程。今天接著介紹一下從(slave)結點是如何發起請求,並通過請求獲取的oplog資訊來構造本地資料的。

    不過開始今天的正文前,需要介紹一下mongodb在slave結點上進行資料同步時的一個大致流程:

1.當一個從結點啟動時,它會對主結點進行一次徹底同步。從結點將複製主結點中的每一個文件(操作量大且耗時)。當初始化的同步完成後,從結點將查詢主結點的oplog並且執行這些操作來保持資料的更新。

2.如從結點上的操作落後主結點太多,從結點處於out-of-sync狀態。該狀態表示從結點不能通過執行同步操作使本地資料趕上主結點資料,因為主結點中的每一個操作都太新了。造成這種情況的原因包括結點宕機或者忙於處理讀請求(儘管mongodb支援讀操作的負載均衡)。如果同步的時間(戳)超出了oplog(滾動)的時間(戳),它將重新開始一次徹底的同步(通過執行resync操作)。


3.當一個從結點處於out-of-sync狀態時,複製將被掛起,從結點需要從主結點進行重新同步。resync流程可以手動執行,即在從結點的admin資料庫上執行命令{“resync”:1}, 或者自動執行:在啟動從結點時使用 --autoresync選項。因為resync是非常操作量大且耗時,最好通過設定一個足夠大的oplogSize來避免resync(預設的oplog大小是空閒磁碟大小的5%)。

    為了驗證上面的流程,下面我們就來看一下slave的執行流程。這裡為了便於除錯,對環境配置如下:

    1.master db ip-> 10.0.4.210
      啟動命令列:d:\mongod>bin>mongod --dbpath=d:\mongodb\db --master --oplogSize 64


    2.在vs中做如下設定(mongod專案屬性視窗):
      --slave --source 10.0.4.210:27017 --only test --slavedelay 100



    因為mongod的主入口函式在db.cpp中,我們可以通過下面方法的呼叫流程找到slave的蛛絲馬跡:

db.cpp-->
     main(
int argc, char* argv[]) //載入啟動引數如--slave,--slavedelay並繫結到replSettings物件void initAndListen(int listenPort, const
char*appserverLoc = NULL)
            
void _initAndListen(int listenPort, constchar*appserverLoc = NULL)
                 listen(
int port)
    當執行到listen()方法後(如下):

複製程式碼 void listen(int port) {
        
//testTheDb();        log() <<"waiting for connections on port "<< port << endl;
        OurListener l(cmdLine.bind_ip, port);
        l.setAsTimeTracker();
        
//啟動複製        startReplication();
        
if ( !noHttpInterface )
            boost::thread web( boost::bind(
&webServerThread, new RestAdminAccess() /* takes ownership */));

        .....
    }
複製程式碼
   mongodb會緊跟著執行repl.cpp檔案中的startReplication()方法,如下:

複製程式碼 //repl.cppvoid startReplication() {
        
/* if we are going to be a replica set, we aren't doing other forms of replication. */if!cmdLine._replSet.empty() ) {
            
//如果使用分佈叢集,則不允許帶slave或master選項if( replSettings.slave || replSettings.master || replPair ) {
                log() 
<<"***"<< endl;
                log() 
<<"ERROR: can't use --slave or --master replication options with --replSet"<< endl;
                log() 
<<"***"<< endl;
            }
            
//繫結函式,oplog.cpp ->_logOpRS(), 用於處理replset型別的oplog操作            newRepl();
            
return;
        }
        
//繫結函式,oplog.cpp ->_logOpOld(), 用於處理master-slave型別的oplog操作        oldRepl();

        
/* this was just to see if anything locks for longer than it should -- we need to be careful
           not to be locked when trying to connect() or query() the other side.
           
*///boost::thread tempt(tempThread);
        
//當設定引數不正確時(非slave,master且不是replPair)if!replSettings.slave &&!replSettings.master &&!replPair )
            
return;

        {
            dblock lk;
            cc().getAuthenticationInfo()
->authorize("admin");
            pairSync
->init();
        }
        
//如果是slave,則開啟相關訪問(master server)執行緒if ( replSettings.slave || replPair ) {
            
if ( replSettings.slave ) {
                assert( replSettings.slave 
== SimpleSlave );
                log(
1<<"slave=true"<< endl;
            }
            
else
                replSettings.slave 
= ReplPairSlave;
            
//構造並啟動執行緒方法replSlaveThread            boost::thread repl_thread(replSlaveThread);
        }
        
//如果是master,則構造並啟動執行緒方法replMasterThreadif ( replSettings.master || replPair ) {
            
if ( replSettings.master )
                log(
1<<"master=true"<< endl;
            replSettings.master 
=true;
            createOplog();//構造oplog集合“local.oplog.$main”
            boost::thread t(replMasterThread);
        }
        
// don't allow writes until we've set up from logwhile( replSettings.fastsync )
            sleepmillis( 
50 );
    }
複製程式碼
    上面方法在完成必要的引數分析檢查之後,就會根據slave的配置資訊來構造啟動執行緒方法replSlaveThread,如下:
複製程式碼 //repl.cppvoid replSlaveThread() {
        sleepsecs(
1);
        Client::initThread(
"replslave");
        cc().iAmSyncThread();

        {
            dblock lk;
            
//獲取認證資訊(mongodb支援使用安全認證不管哪種replicate方式,
            
//只要在master/slave中建立一個能為各個database認識的使用者名稱/密碼即可)            cc().getAuthenticationInfo()->authorize("admin");

            ......
        }

        
while ( 1 ) {
            
try {
                
//repl主函式                replMain();
                
if ( debug_stop_repl )
                    
break;
                sleepsecs(
5);//休眠5秒            }
            
catch ( AssertionException& ) {
                ReplInfo r(
"Assertion in replSlaveThread(): sleeping 5 minutes before retry");
                problem() 
<<"Assertion in replSlaveThread(): sleeping 5 minutes before retry"<< endl;
                sleepsecs(
300);
            }
        }
    }
複製程式碼

    上面方法中有mongodb進行認證的邏輯,之後就會用一個while(1)來迴圈執行(注:每5秒執行一次)replMain()方法,如下:

複製程式碼 //repl.cppvoid replMain() {
        ReplSource::SourceVector sources;
        
while ( 1 ) {
            
int s =0;
            {
                dblock lk;
                
//複製失敗if ( replAllDead ) {//該識別符號會在同步出現異常如(out of sync)時為true
                    
//autoresync:自動地重新執行完整的同步,如果這個從結點脫離了與主結點的同步。if ( !replSettings.autoresync ||!ReplSource::throttledForceResyncDead( "auto" ) )
                        
break;
                }
                
// i.e., there is only one sync thread running.
                
// we will want to change/fix this.                assert( syncing ==0 );
                syncing
++;
            }
            
try {
                
int nApplied =0;
                
//repl主函式                s = _replMain(sources, nApplied);
                
if( s ==1 ) {
                    
if( nApplied ==0 ) s =2;
                    
elseif( nApplied >100 ) {
                        
// sleep very little - just enought that we aren't truly hammering master                        sleepmillis(75);
                        s 
=0;
                    }
                }
            }
            
catch (...) {
                
out() <<"caught exception in _replMain"<< endl;
                s 
=4;
            }
            ......
     }
複製程式碼
     上面程式碼又是一個while(1)迴圈,它會判斷當前slave是否處於“out of sync”狀態,如果是,但未開啟autoresync時,則複製將被掛起。否則會執行_replMain方法來進行同步,如下:

複製程式碼 //repl.cppint _replMain(ReplSource::SourceVector& sources, int& nApplied) {
        {
            ReplInfo r(
"replMain load sources");
            dblock lk;
            ReplSource::loadAll(sources);
//繫結相應的master sources,以便後面遍歷同步/*fastsync: 從主結點的快照中啟動一個從結點。與完整的同步相比,
            這個選項允許一個從結點更快地啟動,如果它的資料目錄已經使用主結點的快照進行初始化。
*/
            replSettings.fastsync 
=false// 初始重置時需要該引數        }
        .....
     
        
int sleepAdvice =1;
        
for ( ReplSource::SourceVector::iterator i = sources.begin(); i != sources.end(); i++ ) {
            ReplSource 
*= i->get();
            
int res =-1;
            
try {
                res 
= s->sync(nApplied);
                
//是否有其它要同步的資料庫bool moreToSync = s->haveMoreDbsToSync();
                
if( res <0 ) {
                    sleepAdvice 
=3;
                }
                
elseif( moreToSync ) {
                    sleepAdvice 
=0;
                }
                
elseif ( s->sleepAdvice() ) {
                    sleepAdvice 
= s->sleepAdvice();
                }
                
else
                    sleepAdvice 
= res;
                
if ( res >=0&&!moreToSync /*&& !s->syncedTo.isNull()*/ ) {
                    pairSync
->setInitialSyncCompletedLocking();
                }
            }
            ......
        }
        
return sleepAdvice;
    }
複製程式碼
    上面的_replMain()方法首先會從“local.sources”資料集中獲取主(master)節點的資訊。這裡解釋一下,mongod從結點在啟動時會將啟動引數(--source)中的資訊儲存到"local.sources"資料集中,並通過此函式(loadAll())中載入到一個集合物件中。這裡我們看一下其載入程式碼,如下:

相關推薦

Mongodb原始碼分析--Replication主從模式--Slave

    在上文中介紹了主從(master-slave)模式下的一些基本概念及master的執行流程。今天接著介紹一下從(slave)結點是如何發起請求,並通過請求獲取的oplog資訊來構造本地資料的。     不過開始今天的正文前,需要介紹一下mongodb在sla

mongodb原始碼分析(十五)replication replset模式的初始化

          相對於主從模式,replset模式複雜得多,其中的主從對應於這裡的primary,secondary概念,primary和 secondary之間可以切換,primary掉線後能夠自動的選取一個secondary成為新的primary,當然這裡也是有 限

mongodb原始碼分析(六)查詢3mongod的cursor的產生

        上一篇文章分析了mongod的資料庫載入部分,下面這一篇文章將繼續分析mongod cursor的產生,這裡cursor 的生成應該是mongodb系統中最複雜的部分.下面先介紹幾個關於mongodb的遊標概念. basicCursor: 直接掃描整個co

mongodb原始碼分析(五)查詢2mongod的資料庫載入

        上一篇文章分析到了客戶端查詢請求的傳送,接著分析服務端的處理動作,分析從服務端響應開始到資料庫 正確載入止,主要流程為資料庫的讀入過程與使用者的認證.         mongod服務對於客戶端請求的處理在mongo/db/db.cpp MyMessageH

Dubbo 原始碼分析系列三 —— 架構原理

1 核心功能 首先要了解Dubbo提供的三大核心功能: Remoting:遠端通訊 提供對多種NIO框架抽象封裝,包括“同步轉非同步”和“請求-響應”模式的資訊交換方式。 Cluster: 服務框架 提供基於介面方法的透明遠端過程呼叫,包括多協議支援,以及

Java程式設計師從笨鳥到菜鳥(八十一)細談Spring(十)深入原始碼分析SpringHibernateTemplate

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Tomcat 原始碼分析系列環境搭建

Tomcat 原始碼環境搭建 tomcat 9 和 idea 環境搭建 環境準備 JDK 1.10 git idea tomcat 原始碼 maven ant 國內的maven 倉庫映象 安裝Intellij Idea 新

原始碼分析手寫springvc

1.先建立maven的web專案,結構如下 2.在web.xml新增如下配置 <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>com.mayik

RxJava2原始碼分析just、fromArray、fromIterable

     Observable.just:接收1個以上,10個以下的引數,然後逐個發射。         Observable.fromArray:接收一個數組,從陣列中一個一個取出來發射。  

jQuery2.0.3原始碼分析系列(29) 視窗尺寸

.width() 基礎回顧 一般的,在獲取瀏覽器視窗的大小和位置時,有以下幾個屬性可以使用: 在不同的瀏覽器中,以下12個屬性所代表的意義也是不一樣的 特別需要注意的是,當使用或者不使用<!DOCTYPE>宣告顯示一個文件的時候,以上12個屬性的意義也會發生變化。 特在IE 9中

原始碼分析String原始碼分析

    前面已經分析過String原始碼為什麼是不可變的,同時通過我們常用的String的相關的類StringBuffer和StringBuilder,我們可以發現String類中欄位名被定義為了final型別,這樣的話將只能被賦值一次。接下來,繼續看String原始碼實現的

Android wpa_supplicant原始碼分析--啟動全域性初始化

1. wpa_supplicant簡介 wpa_supplicant是用來用來支援無線中各種加密方式的,包括WEP、WPA/WPA2和WAPI(中國特有)、EAP(8021x)。wpa_s通過socket與上層(framework)和底層(driver)通訊,向上接收命令和傳

RabbitMQ客戶端原始碼分析(三)Command

RabbitMQ-java-client版本 com.rabbitmq:amqp-client:4.3.0 RabbitMQ版本宣告: 3.6.15 Command Command介面是AMQP方法-引數的容器介面,帶有可選的內容頭(content

RabbitMQ客戶端原始碼分析(五)ConsumerWorkSerivce與WorkPool

RabbitMQ-java-client版本 com.rabbitmq:amqp-client:4.3.0 RabbitMQ版本宣告: 3.6.15 WorkPool WorkPool可以認

RabbitMQ客戶端原始碼分析(六)IntAllocator

RabbitMQ-java-client版本 com.rabbitmq:amqp-client:4.3.0 RabbitMQ版本宣告: 3.6.15 IntAllocator 用於分配給定範

RabbitMQ客戶端原始碼分析(七)Channel與ChannelManager

RabbitMQ-java-client版本 com.rabbitmq:amqp-client:4.3.0 RabbitMQ版本宣告: 3.6.15 Channel uml圖 tran

原始碼分析String原始碼分析

    上期已經討論了比較器的相關的問題。同時也提出問題明明父類已經實現了某個介面,子類為什麼還是要實現同樣的介面呢。這究竟有什麼意義呢。通過使用度孃的多次探究之後最終我還是沒有發現說它有什麼特別的含義。我認為的最大的含義就是告訴使用者這個類實現了這樣的一個介面。方便使用者進

RabbitMQ客戶端原始碼分析(九)RPC請求響應

宣告 Queue宣告、exchange宣告、bind等,這些都是通過同步RPC呼叫 channel.queueDeclare(queueName, durable

RocketMQ原始碼分析(一)除錯環境搭建

版本宣告 基於rocketmq-all-4.3.1版本 所有分析是根據自己的理解,因為不是RocketMQ的原始開發者,所以肯定會存在分析不正確的地方,如有發現歡迎指正,謝謝! 規定$ROCKET

原始碼分析KafkaProducer

我們來看看訊息類ProducerRecord有哪些屬性: private final String topic;//主題 private final Integer partition;//分割槽 private final Headers headers;//頭 private final K ke