導讀:1.3版本的PHP MongoDB driver重寫了連接處理庫,和以前版本相比,在持久連接和連接池方面,都有了重大的變化。
1.2版本的連接管理
1.2版本的驅動引入了連接池,在執行任何查詢時,都會從連接池中請求一個連接,完成之后再歸還給連接池。這里的完成是指持有該連接的變量離開了它的作用域,下面是一個示例。
最簡單的版本:
<?php
$m = new MongoClient(); // ← 從連接池請求連接
$c = $m->demo->test;
$c->insert( array( 'test' => 'yes' ) );
?>
← $m離開作用域,連接歸還給連接池
在函數中:
<?php
function doQuery()
{
$m = new MongoClient(); // ← 從連接池請求連接
$c = $m->demo->test;
$c->insert( array( 'test' => 'yes' ) );
} // ← $m離開作用域,連接歸還給連接池
?>
在某些情況下,系統可能會產生大量的連接,比如在ORMs/ODMs的某個復雜結構中引用連接對象,如下例子:
<?php
for ( $i = 0; $i < 5; $i++ )
{
$conns[] = new MongoClient();
}// ← 現在有5個連接
?>
1.3版本的連接管理
在1.3版本中,連接管理做了很大改動。每個worker進程(線程、PHP-FPM或Apache worker)中,驅動把連接管理和Mongo*對象分離,降低驅動的復雜度。下面以單個節點的MongoDB實例來說明驅動如何處理連接。
當一個worker進程啟動,MongoDB驅動會為之初始化連接管理器管理連接,并且默認沒有連接。
在第一個請求調用new MongoClient();時,驅動創建一個新連接,并且以一個哈希值標識這個連接。這個哈希值包括以下參數:主機名、端口,進程ID和可選的replica set名,如果是密碼驗證的連接,則還包括數據庫名、用戶名和密碼的哈希值(對于密碼驗證的連接,我們后面再詳細討論)。調用MongoClient::getConnections()方法,可以查看連接對應的哈希值:
<?php
$m = new MongoClient( 'mongodb://whisky:27017/' );
var_dump( $m->getConnections()[0]['hash'] );
?>
輸出:
string(22) “whisky:27017;-;X;22835″
輸出中的”-”表示該連接不屬于某個replica set,”X”是沒有用戶名、數據庫和密碼時的占位符,22835是當前進程的進程ID。
然后該連接會在連接管理器中注冊:
在需要連接的任何時候,包括插入、刪除、更新、查找或執行命令,驅動都會向管理器請求一個合適的連接來執行。請求連接時會用到new MongoClient()的參數和當前進程的ID。每個worker進程/線程,連接管理器都會有一個連接列表,而每個PHP worker同一時刻,只會運行一個請求,因此和每個MongoDB之間只需要一個連接,不斷重用,直到PHP worker終止或顯式調用MongoClient::close()關閉連接。
Replica sets
在存在復制集的環境中,情形有點不一樣。new MongoClient()的連接字符串中,需要指定多個hosts,并標示當前正在實用復制集:
$m = new MongoClient(“mongodb://whisky:13000,whisky:13001/?replicaSet=seta”);
其中的replicaSet參數不能省略,否則驅動會認為你是準備連接三個不同的mongos進程。
在實例化時,驅動會檢查復制集的拓撲結構。下面例子的輸出,顯示在調用new MongoClient()之后,復制集中所有可見的數據節點都會在管理器中注冊一個連接:
<?php
$m = new MongoClient( 'mongodb://whisky:13001/?replicaSet=seta' );
foreach ( $m->getConnections() as $c )
{
echo $c['hash'], "\n";
}
?>
輸出:
whisky:13001;seta;X;32315 whisky:13000;seta;X;32315
雖然連接字符串中沒有whisky:13000節點,但是管理器中已經注冊了兩個連接:
管理器不僅包含連接的哈希值和TCP/IP socket,還保存哪個節點是主節點,以及每個節點的“距離”。下面的腳本顯示了這些額外的信息;
<?php
$m = new MongoClient( 'mongodb://whisky:13001/?replicaSet=seta' );
foreach ( $m->getConnections() as $c )
{
echo $c['hash'], ":\n",
" - {$c['connection']['connection_type_desc']}, ",
"{$c['connection']['ping_ms']} ms\n";
}
?>
輸出:
whisky:13001;seta;X;5776: – SECONDARY, 1 ms whisky:13000;seta;X;5776: – PRIMARY, 0 ms
驅動把操作分為兩種類型:寫操作,包括插入、更新、刪除和命令;讀操作,包括find和findOne。默認情況下,如果沒有設置讀偏好參數,管理器會一直返回主節點的連接。讀偏好參數可以通過setSlaveOkay()設置,也可以在連接字符串中設置:
$m = new MongoClient("mongodb://whisky:13000,whisky:13001/?replicaSet=seta&readPreference=secondaryPreferred");
加上這些參數后,連接字符串變得特別長,因此PHP驅動允許將選項放在數組中,作為第二個參數傳入:
$options = array(
'replicaSet' => 'seta',
'readPreference' => 'secondaryPreferred',
);
$m = new MongoClient("mongodb://whisky:13000,whisky:13001/", $options);
對于每個操作,驅動向管理器請求獲取一個合適的連接。對于寫操作,會一直返回主節點的連接;對于讀操作,如果輔助節點可用且“距離”不遠的話,則會返回該輔助節點的連接。
驗證的連接
如果MongoDB啟用驗證功能,那么連接的哈希值會包含驗證相關的哈希值。這樣不同腳本,使用不同的用戶名、密碼連接同一個MongoDB上的不同的數據庫時,能夠相互區分,而不會誤用連接。下面示例使用admin用戶名連接admin數據庫,然后觀察hash值的變化:
<?php
$m = new MongoClient( 'mongodb://admin:admin@whisky:27017/admin' );
var_dump( $m->getConnections()[0]['hash'] );
?>
輸出:
string(64) “whisky:27017;-;admin/admin/bda5cc70cd5c23f7ffa1fda978ecbD30;8697″
以前示例中的”X”部分已經替換為一個包含數據庫名admin、用戶名admin和哈希值bda5cc70cd5c23f7ffa1fda978ecbd30,該哈希值是根據用戶名、數據庫名和密碼哈希值計算得來。
為了驗證能夠正確工作,需要在連接字符串中包含數據庫名,否則會默認為admin。
在建立連接后要使用數據庫,需要先選擇該數據庫,如:
$collection = $m->demoDb->collection; $collection->findOne();
如果選擇的數據庫是連接字符串中指定的數據庫,或者連接字符串中的數據庫是admin,那么一切都會正常運行。否則,驅動會創建一個新的連接,從而防止驗證被繞過,如下所示:
<?php
$m = new MongoClient( 'mongodb://user:user@whisky:27017/test' );
$db = $m->test2;
$collection = $db->collection;
var_dump( $collection->findOne() );
?>
輸出:
Fatal error: Uncaught exception ‘MongoCursorException’ with message ‘whisky:27017: unauthorized db:test2 ns:test2.collection lock type:0 client:127.0.0.1′ in …/mongo-connect-5.php.txt:6
因為我們的連接并沒有執行test2數據庫的授權驗證,因而失敗。如果我們執行驗證,就會正常運行:
<?php
$m = new MongoClient( 'mongodb://user:user@whisky:27017/test' );
$db = $m->test2;
$db->authenticate('user2', 'user2' );
$collection = $db->collection;
$collection->findOne();
foreach ( $m->getConnections() as $c )
{
echo $c['hash'], "\n";
}
?>
輸出:
whisky:27017;-;test/user/602b672e2fdcda7b58a042aeeb034376;26983 whisky:27017;-;test2/user2/984b6b4fd6c33f49b73f026f8b47c0de;26983
現在管理器中有兩個已驗證的連接:
順便提一句,如果你打開了E_DEPRECATED級別的錯誤提示,則會看到:
Deprecated: Function MongoDB::authenticate() is deprecated in …/mongo-connect-6.php.txt on line 5
驅動建議通過創建兩個MongoClient對象完成該類任務:
<?php
$mTest1 = new MongoClient( 'mongodb://user:user@whisky:27017/test', array( 'connect' => false ) );
$mTest2 = new MongoClient( 'mongodb://user2:user2@whisky:27017/test2', array( 'connect' => false ) );
$mTest1->test->test->findOne();
$mTest2->test2->test->findOne();
foreach ( $mTest2->getConnections() as $c )
{
echo $c['hash'], "\n";
}
?>
單個MongoDB服務器能支持的并發連接相當有限,如果使用PHP-FPM的話,每個worker進程有自己獨立的連接池,那么很容易達到連接數的上限。因此,在生產環境中,不管有沒有使用復制集,都要部署mongos,然后PHP-FPM連接mongos,這樣可以減少mongod的連接數,并且PHP-FPM和mongos之間可以使用短連接(即每個請求結束時都顯式調用close函數關閉MongoDB連接)。
來源:http://wulijun.github.io/2012/12/10/mongodb-php-driver-connectiong-handling.html
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜