在之前的一篇文章中,介紹了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