程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Mongodb源碼分析--鏈接池(ConnPool)

Mongodb源碼分析--鏈接池(ConnPool)

編輯:關於C語言

 在之前的一篇文章中,介紹了mongos的balaner的執行流程,其中在源碼中的Balancer::run()方法裡簡單說明了為了連接到configserver,balancer通過構造ScopedDbConnection實現來鏈接並執行相應操作,因為當時篇幅所限,只是該鏈接使用池化的方式一帶而過,今天就專門介紹一下mongodb中使用池化方式來管理鏈接對象以提升鏈接效率的原理。
   
    好了,開始今天的正文吧!

    首先看一下balancer類的run()方法,相關代碼如下:   
   

    //balance.cpp
    void Balancer::run() {
        ......
        while ( ! inShutdown() ) {//一直循環直到程序中斷或關閉

            try {
                ......
               
                ScopedDbConnection conn( config );
              
                ......
          
                conn.done();//將conn放到鏈接池中(為其它後續操作使用)

                sleepsecs( _balancedLastTime ? 5 : 10 );
            }
            catch ( std::exception& e ) {
                ......
            }
        }
    }
    上面方法中從ScopedDbConnection聲明到該實現執行done()方法結束,系統會從鏈接池中獲取一個鏈接對象,如無鏈接則直接創建。如果是創建的鏈接,則會將該鏈接添加到池中。下面我們就看一下其類圖:

\
 
    圖中的紅框所圈的類均為connpool.h頭文件中所包含定義的類信息,而這些類中(比如ScopedDbConnection,上面代碼提到過)會包含一個DBClientBase屬性指針,而DBClientBase的定義位於dbclient.h頭文件中,其主要是定義了客戶端連接到mongodb服務端時所經常進行的操作(CRUD等)。

    圖中的類比較多,主要的幾個包括:  


    ScopedDbConnection:池中的數據庫鏈接類,其通過持有的DBClientBase指出針來施加crud操作
    DBConnectionPool:數據庫鏈接池類,定義鏈接的創建,獲取,flush,以及維護等操作。
    PoolForHost:該對象提供以棧式(stack)方式管理pool鏈接對象。
   
    下面就先看一下ScopedDbConnection的構造方法,其執行流程如下:


    //connpool.cpp
    ScopedDbConnection::ScopedDbConnection(const Shard& shard )
        : _host( shard.getConnString() ) , _conn( pool.get(_host) ) {
    }


    其中的_host( shard.getConnString() )只是將要鏈接的mongo服務地址綁定到ScopedDbConnection的_host屬性上。重要的是_conn( pool.get(_host))這一行代碼,它會從池中(pool類型為DBConnectionPool)獲取一個鏈接,如池中沒有則會創建一個鏈接並返回,如下(詳情見注釋):


    //connpool.cpp
    DBClientBase* DBConnectionPool::get(const ConnectionString& url) {
        // 從池中獲取一個鏈接對象
        DBClientBase * c = _get( url.toString() );
        //如獲取到則直接返回
        if ( c ) {
            onHandedOut( c );//執行取出時定義的hook方法
            return c;
        }

        string errmsg;
        c = url.connect( errmsg );
        uassert( 13328 ,  _name + ": connect failed " + url.toString() + " : " + errmsg , c );
        //以url為鏈接地址,構造一個鏈接對象並返回該對象
        return _finishCreate( url.toString() , c );
    }


    上面方法中_get( url.toString() ) 這一行代碼主要是用於執行從池中獲取對象的操作,它的實現代碼如下:
   


    DBClientBase* DBConnectionPool::_get(const string& ident) {
        scoped_lock L(_mutex);
        PoolForHost& p = _pools[ident];//獲取指定的鏈接池
        return p.get();
    }


    其中_pools類型定義如下,用於實現從“服務器名稱”到“相應鏈接池”的映射,因為不同的服務器會對應不同的鏈接池:


       typedef map<string,PoolForHost,serverNameCompare> PoolMap;

   
    找到了相應的鏈接池之後,返回該池所對應的PoolForHost對象的引用,該對象提供以棧式(stack)方式管理pool鏈接對象。其get()方法定義如下:

 

    //connpool.cpp
    DBClientBase * PoolForHost::get() {
        time_t now = time(0);

        while ( ! _pool.empty() ) {
            StoredConnection sc = _pool.top();//取出棧頂鏈接
            _pool.pop();//移除棧頂的元素
            if ( sc.ok( now ) )//如鏈接空閒未超過1小時
                return sc.conn;
            delete sc.conn; //釋放鏈接對象
        }
        return NULL;//如無有效鏈接,則返回null
    }


    現在我們再將注意力放回到主流程DBClientBase* DBConnectionPool::get(const ConnectionString& url)方法的下面一行代碼,即:


    //connpool.cpp
    //如獲取到則直接返回
    if ( c ) {
            onHandedOut( c );//執行取出時定義的hook方法
            return c;
    }


    該方法一個hook方法的調用,它的實現方式有些復雜,很像設置模式中的Observer (觀察者)模式,我們先看一下該模式的類圖:

    \

    有關該模式的具體講解可以參見相關資料或在google上搜一下,這裡暫不做解釋了。
   
    這裡我們先看一下該方法的具體實現(onCreate與onHandedOut方式類似,這裡僅對onHandedOut進行說明):
   


vo

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved