MongoDB PHP Driver的連線處理解析
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連線)。
相關文章
- 通過php的MongoDB driver連線Azure的DocumentDB PaaSPHPMongoDB
- php連線mongodbPHPMongoDB
- MongoDB Driver:使用正確的姿勢連線分片叢集MongoDB
- Node.js 服務連線 MongoDB 處理最佳實踐Node.jsMongoDB
- JDBC Driver連線方法列表JDBC
- 處理方塊之間的連線線
- 行連線的處理方式指引
- 解析PHP處理換行符的問題PHP
- PHP-fpm MongoDB 連線數爆了問題PHPMongoDB
- mongodb連線字串MongoDB字串
- MongoDB故障處理MongoDB
- postgresql連線失敗如何處理SQL
- windows 處理bat連線本地mysqlWindowsBATMySql
- SpringMVC 解析(五)URI連結處理SpringMVC
- python與MongoDB的連線PythonMongoDB
- mongodb-java-driver的基本用法MongoDBJava
- BIRT 如何連線 MongoDBMongoDB
- sql server連線排序衝突處理SQLServer排序
- SSH 連線緩慢問題處理
- asp連線Mysql及編碼處理MySql
- 多表連線SQL優化如何處理SQL優化
- vnc遠端連線黑屏,vnc連線Linux後黑屏的處理辦法VNCLinux
- Netty是如何處理新連線接入事件的?Netty事件
- pymysql 處理 連線超時最好的解決方案MySql
- mysql自動斷開連線的問題處理MySql
- node.js連線mongodbNode.jsMongoDB
- Mongodb資料庫連線MongoDB資料庫
- Spark連線MongoDB之ScalaSparkMongoDB
- mongodb-java-driver基本用法MongoDBJava
- linux如何處理多連線請求?Linux
- JDBC連線批量處理資料入庫JDBC
- 資料庫連線異常處理思路資料庫
- Mongodb請求處理流程MongoDB
- 例項解析外連線 內連線 自連線 全連線
- PHP 連線 OraclePHPOracle
- PHP 連線oraclePHPOracle
- php連線kafkaPHPKafka
- Oracle優化器內部處理的表連線方式Oracle優化